xref: /reactos/drivers/storage/class/classpnp/class.c (revision 34593d93)
1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 2010
4 
5 Module Name:
6 
7     class.c
8 
9 Abstract:
10 
11     SCSI class driver routines
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #define CLASS_INIT_GUID 1
25 #define DEBUG_MAIN_SOURCE 1
26 
27 #include "classp.h"
28 #include "debug.h"
29 #include <process.h>
30 #include <devpkey.h>
31 #include <ntiologc.h>
32 
33 
34 #ifdef DEBUG_USE_WPP
35 #include "class.tmh"
36 #endif
37 
38 #ifdef ALLOC_PRAGMA
39     #pragma alloc_text(INIT, DriverEntry)
40     #pragma alloc_text(PAGE, ClassAddDevice)
41     #pragma alloc_text(PAGE, ClassClaimDevice)
42     #pragma alloc_text(PAGE, ClassCreateDeviceObject)
43     #pragma alloc_text(PAGE, ClassDispatchPnp)
44     #pragma alloc_text(PAGE, ClassGetDescriptor)
45     #pragma alloc_text(PAGE, ClassGetPdoId)
46     #pragma alloc_text(PAGE, ClassInitialize)
47     #pragma alloc_text(PAGE, ClassInitializeEx)
48     #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
49     #pragma alloc_text(PAGE, ClassMarkChildMissing)
50     #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
51     #pragma alloc_text(PAGE, ClassModeSense)
52     #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
53     #pragma alloc_text(PAGE, ClassPnpStartDevice)
54     #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
55     #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
56     #pragma alloc_text(PAGE, ClassRemoveDevice)
57     #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
58     #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
59     #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
60     #pragma alloc_text(PAGE, ClassUnload)
61     #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
62     #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
63     #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
64     #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
65     #pragma alloc_text(PAGE, ClasspScanForClassHacks)
66     #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
67     #pragma alloc_text(PAGE, ClasspModeSense)
68     #pragma alloc_text(PAGE, ClasspIsPortable)
69     #pragma alloc_text(PAGE, ClassAcquireChildLock)
70     #pragma alloc_text(PAGE, ClassDetermineTokenOperationCommandSupport)
71     #pragma alloc_text(PAGE, ClassDeviceProcessOffloadRead)
72     #pragma alloc_text(PAGE, ClassDeviceProcessOffloadWrite)
73     #pragma alloc_text(PAGE, ClasspServicePopulateTokenTransferRequest)
74     #pragma alloc_text(PAGE, ClasspServiceWriteUsingTokenTransferRequest)
75 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
76     #pragma alloc_text(PAGE, ClassModeSenseEx)
77 #endif
78 #endif
79 
80 #ifdef _MSC_VER
81 #pragma prefast(disable:28159, "There are certain cases when we have to bugcheck...")
82 #endif
83 
84 IO_COMPLETION_ROUTINE ClassCheckVerifyComplete;
85 
86 
87 ULONG ClassPnpAllowUnload = TRUE;
88 ULONG ClassMaxInterleavePerCriticalIo = CLASS_MAX_INTERLEAVE_PER_CRITICAL_IO;
89 CONST LARGE_INTEGER Magic10000 = {{0xe219652c, 0xd1b71758}};
90 GUID StoragePredictFailureDPSGuid = WDI_STORAGE_PREDICT_FAILURE_DPS_GUID;
91 
92 #define FirstDriveLetter 'C'
93 #define LastDriveLetter  'Z'
94 
95 BOOLEAN UseQPCTime = FALSE;
96 
97 //
98 // Keep track of whether security cookie is initialized or not. This is
99 // required by SDL.
100 //
101 
102 BOOLEAN InitSecurityCookie = FALSE;
103 
104 //
105 // List Identifier for offload data transfer operations
106 //
107 ULONG MaxTokenOperationListIdentifier = MAX_TOKEN_LIST_IDENTIFIERS;
108 volatile ULONG TokenOperationListIdentifier = (ULONG)-1;
109 
110 //
111 // List of FDOs that have enabled idle power management.
112 //
113 LIST_ENTRY IdlePowerFDOList = {0};
114 KGUARDED_MUTEX IdlePowerFDOListMutex;
115 
116 //
117 // Handle used to register for power setting notifications.
118 //
119 PVOID PowerSettingNotificationHandle;
120 
121 //
122 // Handle used to register for screen state setting notifications.
123 //
124 PVOID ScreenStateNotificationHandle;
125 
126 //
127 // Disk idle timeout in milliseconds.
128 // We default this to 0xFFFFFFFF as this is what the power manager considers
129 // "never" and ensures we do not set a disk idle timeout until the power
130 // manager calls us back with a different value.
131 //
132 ULONG DiskIdleTimeoutInMS = 0xFFFFFFFF;
133 
134 
DllUnload(VOID)135 NTSTATUS DllUnload(VOID)
136 {
137     DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_INFO_LEVEL, "classpnp.sys is now unloading\n");
138 
139     if (PowerSettingNotificationHandle) {
140         PoUnregisterPowerSettingCallback(PowerSettingNotificationHandle);
141         PowerSettingNotificationHandle = NULL;
142     }
143 
144     if (ScreenStateNotificationHandle) {
145         PoUnregisterPowerSettingCallback(ScreenStateNotificationHandle);
146         ScreenStateNotificationHandle = NULL;
147     }
148 
149 
150     return STATUS_SUCCESS;
151 }
152 
153 
154 /*++////////////////////////////////////////////////////////////////////////////
155 
156 DriverEntry()
157 
158 Routine Description:
159 
160     Temporary entry point needed to initialize the class system dll.
161     It doesn't do anything.
162 
163 Arguments:
164 
165     DriverObject - Pointer to the driver object created by the system.
166 
167 Return Value:
168 
169    STATUS_SUCCESS
170 
171 --*/
172 NTSTATUS
173 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)174 DriverEntry(
175     IN PDRIVER_OBJECT DriverObject,
176     IN PUNICODE_STRING RegistryPath
177     )
178 {
179     UNREFERENCED_PARAMETER(DriverObject);
180     UNREFERENCED_PARAMETER(RegistryPath);
181 
182     return STATUS_SUCCESS;
183 }
184 
185 
186 
187 /*++////////////////////////////////////////////////////////////////////////////
188 
189 ClassInitialize()
190 
191 Routine Description:
192 
193     This routine is called by a class driver during its
194     DriverEntry routine to initialize the driver.
195 
196 Arguments:
197 
198     Argument1          - Driver Object.
199     Argument2          - Registry Path.
200     InitializationData - Device-specific driver's initialization data.
201 
202 Return Value:
203 
204     A valid return code for a DriverEntry routine.
205 
206 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)207 _IRQL_requires_max_(PASSIVE_LEVEL)
208 _Must_inspect_result_
209 ULONG
210 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
211 ClassInitialize(
212     _In_  PVOID            Argument1,
213     _In_  PVOID            Argument2,
214     _In_  PCLASS_INIT_DATA InitializationData
215     )
216 {
217     PDRIVER_OBJECT  DriverObject = Argument1;
218     PUNICODE_STRING RegistryPath = Argument2;
219 
220     PCLASS_DRIVER_EXTENSION driverExtension;
221 
222     NTSTATUS        status;
223 
224 
225 
226     PAGED_CODE();
227 
228     //
229     // Initialize the security cookie if needed.
230     //
231 #ifndef __REACTOS__
232     if (InitSecurityCookie == FALSE) {
233         __security_init_cookie();
234         InitSecurityCookie = TRUE;
235     }
236 #endif
237 
238 
239     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "\n\nSCSI Class Driver\n"));
240 
241     ClasspInitializeDebugGlobals();
242 
243     //
244     // Validate the length of this structure. This is effectively a
245     // version check.
246     //
247 
248     if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
249 
250         //
251         // This DebugPrint is to help third-party driver writers
252         //
253 
254         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class driver wrong version\n"));
255         return (ULONG) STATUS_REVISION_MISMATCH;
256     }
257 
258     //
259     // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
260     // are not required entry points.
261     //
262 
263     if ((!InitializationData->FdoData.ClassDeviceControl) ||
264         (!((InitializationData->FdoData.ClassReadWriteVerification) ||
265            (InitializationData->ClassStartIo))) ||
266         (!InitializationData->ClassAddDevice) ||
267         (!InitializationData->FdoData.ClassStartDevice)) {
268 
269         //
270         // This DebugPrint is to help third-party driver writers
271         //
272 
273         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
274             "ClassInitialize: Class device-specific driver missing required "
275             "FDO entry\n"));
276 
277         return (ULONG) STATUS_REVISION_MISMATCH;
278     }
279 
280     if ((InitializationData->ClassEnumerateDevice) &&
281         ((!InitializationData->PdoData.ClassDeviceControl) ||
282          (!InitializationData->PdoData.ClassStartDevice) ||
283          (!((InitializationData->PdoData.ClassReadWriteVerification) ||
284             (InitializationData->ClassStartIo))))) {
285 
286         //
287         // This DebugPrint is to help third-party driver writers
288         //
289 
290         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "ClassInitialize: Class device-specific missing "
291                        "required PDO entry\n"));
292 
293         return (ULONG) STATUS_REVISION_MISMATCH;
294     }
295 
296     if((InitializationData->FdoData.ClassStopDevice == NULL) ||
297         ((InitializationData->ClassEnumerateDevice != NULL) &&
298          (InitializationData->PdoData.ClassStopDevice == NULL))) {
299 
300         //
301         // This DebugPrint is to help third-party driver writers
302         //
303 
304         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "ClassInitialize: Class device-specific missing "
305                        "required PDO entry\n"));
306         NT_ASSERT(FALSE);
307         return (ULONG) STATUS_REVISION_MISMATCH;
308     }
309 
310     //
311     // Setup the default power handlers if the class driver didn't provide
312     // any.
313     //
314 
315     if(InitializationData->FdoData.ClassPowerDevice == NULL) {
316         InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
317     }
318 
319     if((InitializationData->ClassEnumerateDevice != NULL) &&
320        (InitializationData->PdoData.ClassPowerDevice == NULL)) {
321         InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
322     }
323 
324     //
325     // warn that unload is not supported
326     //
327     // ISSUE-2000/02/03-peterwie
328     // We should think about making this a fatal error.
329     //
330 
331     if(InitializationData->ClassUnload == NULL) {
332 
333         //
334         // This DebugPrint is to help third-party driver writers
335         //
336 
337         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,  "ClassInitialize: driver does not support unload %wZ\n",
338                     RegistryPath));
339     }
340 
341     //
342     // Create an extension for the driver object
343     //
344 #ifdef _MSC_VER
345 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
346 #endif
347     status = IoAllocateDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY, sizeof(CLASS_DRIVER_EXTENSION), (PVOID *)&driverExtension);
348 
349     if(NT_SUCCESS(status)) {
350 
351         //
352         // Copy the registry path into the driver extension so we can use it later
353         //
354 
355         driverExtension->RegistryPath.Length = RegistryPath->Length;
356         driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
357 
358         driverExtension->RegistryPath.Buffer =
359             ExAllocatePoolWithTag(PagedPool,
360                                   RegistryPath->MaximumLength,
361                                   '1CcS');
362 
363         if(driverExtension->RegistryPath.Buffer == NULL) {
364 
365             status = STATUS_INSUFFICIENT_RESOURCES;
366             return status;
367         }
368 
369         RtlCopyUnicodeString(
370             &(driverExtension->RegistryPath),
371             RegistryPath);
372 
373         //
374         // Copy the initialization data into the driver extension so we can reuse
375         // it during our add device routine
376         //
377 
378         RtlCopyMemory(
379             &(driverExtension->InitData),
380             InitializationData,
381             sizeof(CLASS_INIT_DATA));
382 
383         driverExtension->DeviceCount = 0;
384 
385         ClassInitializeDispatchTables(driverExtension);
386 
387     } else if (status == STATUS_OBJECT_NAME_COLLISION) {
388 
389         //
390         // The extension already exists - get a pointer to it
391         //
392 #ifdef _MSC_VER
393 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
394 #endif
395         driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
396 
397         NT_ASSERT(driverExtension != NULL);
398 
399     } else {
400 
401         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "ClassInitialize: Class driver extension could not be "
402                        "allocated %lx\n", status));
403         return status;
404     }
405 
406 
407     //
408     // Update driver object with entry points.
409     //
410 
411 #ifdef _MSC_VER
412 #pragma prefast(push)
413 #pragma prefast(disable:28175, "Accessing DRIVER_OBJECT fileds is OK here since this function " \
414                                "is supposed to be invoked from DriverEntry only")
415 #endif
416     DriverObject->MajorFunction[IRP_MJ_CREATE]          = ClassGlobalDispatch;
417     DriverObject->MajorFunction[IRP_MJ_CLOSE]           = ClassGlobalDispatch;
418     DriverObject->MajorFunction[IRP_MJ_READ]            = ClassGlobalDispatch;
419     DriverObject->MajorFunction[IRP_MJ_WRITE]           = ClassGlobalDispatch;
420     DriverObject->MajorFunction[IRP_MJ_SCSI]            = ClassGlobalDispatch;
421     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = ClassGlobalDispatch;
422     DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]        = ClassGlobalDispatch;
423     DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]   = ClassGlobalDispatch;
424     DriverObject->MajorFunction[IRP_MJ_PNP]             = ClassGlobalDispatch;
425     DriverObject->MajorFunction[IRP_MJ_POWER]           = ClassGlobalDispatch;
426     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]  = ClassGlobalDispatch;
427 
428     if (InitializationData->ClassStartIo) {
429         DriverObject->DriverStartIo = ClasspStartIo;
430     }
431 
432     if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) {
433         DriverObject->DriverUnload = ClassUnload;
434     } else {
435         DriverObject->DriverUnload = NULL;
436     }
437 
438     DriverObject->DriverExtension->AddDevice = ClassAddDevice;
439 #ifdef _MSC_VER
440 #pragma prefast(pop)
441 #endif
442 
443 
444     //
445     // Register for event tracing
446     //
447     if (driverExtension->EtwHandle == 0) {
448         status = EtwRegister(&StoragePredictFailureDPSGuid,
449                              NULL,
450                              NULL,
451                              &driverExtension->EtwHandle);
452         if (!NT_SUCCESS(status)) {
453             driverExtension->EtwHandle = 0;
454         }
455         WPP_INIT_TRACING(DriverObject, RegistryPath);
456     }
457 
458 
459     //
460     // Ensure these are only initialized once.
461     //
462     if (IdlePowerFDOList.Flink == NULL) {
463         InitializeListHead(&IdlePowerFDOList);
464         KeInitializeGuardedMutex(&IdlePowerFDOListMutex);
465     }
466 
467     status = STATUS_SUCCESS;
468     return status;
469 } // end ClassInitialize()
470 
471 /*++////////////////////////////////////////////////////////////////////////////
472 
473 ClassInitializeEx()
474 
475 Routine Description:
476 
477     This routine is allows the caller to do any extra initialization or
478     setup that is not done in ClassInitialize. The operation is
479     controlled by the GUID that is passed and the contents of the Data
480     parameter is dependent upon the GUID.
481 
482     This is the list of supported operations:
483 
484     GUID_CLASSPNP_QUERY_REGINFOEX == CLASS_QUERY_WMI_REGINFO_EX_LIST
485 
486         Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
487         callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
488         former callback allows the driver to specify the name of the
489         mof resource.
490 
491     GUID_CLASSPNP_SENSEINFO2      == CLASS_INTERPRET_SENSE_INFO2
492 
493         Initialize classpnp to callback into class drive for interpretation
494         of all sense info, and to indicate the count of "history" to keep
495         for each packet.
496 
497     GUID_CLASSPNP_WORKING_SET     == CLASS_WORKING_SET
498 
499         Allow class driver to override the min and max working set transfer
500         packet value used in classpnp.
501 
502     GUID_CLASSPNP_SRB_SUPPORT     == ULONG
503 
504         Allow class driver to provide supported SRB types.
505 
506 Arguments:
507 
508     DriverObject
509     Guid
510     Data
511 
512 Return Value:
513 
514     Status Code
515 
516 --*/
_IRQL_requires_max_(PASSIVE_LEVEL)517 _IRQL_requires_max_(PASSIVE_LEVEL)
518 _Must_inspect_result_
519 ULONG
520 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
521 ClassInitializeEx(
522     _In_  PDRIVER_OBJECT   DriverObject,
523     _In_  LPGUID           Guid,
524     _In_  PVOID            Data
525     )
526 {
527     PCLASS_DRIVER_EXTENSION driverExtension;
528 
529     NTSTATUS        status;
530 
531     PAGED_CODE();
532 
533 #ifdef _MSC_VER
534 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
535 #endif
536     driverExtension = IoGetDriverObjectExtension( DriverObject, CLASS_DRIVER_EXTENSION_KEY );
537 
538     if (driverExtension == NULL)
539     {
540         NT_ASSERT(FALSE);
541         return (ULONG)STATUS_UNSUCCESSFUL;
542     }
543 
544     if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
545     {
546         PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
547 
548         //
549         // Indicate the device supports PCLASS_QUERY_REGINFO_EX
550         // callback instead of PCLASS_QUERY_REGINFO callback.
551         //
552         List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
553 
554         if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
555         {
556             driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
557             driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
558             status = STATUS_SUCCESS;
559         } else {
560             status = STATUS_INVALID_PARAMETER;
561         }
562     }
563     else if (IsEqualGUID(Guid, &ClassGuidWorkingSet))
564     {
565         PCLASS_WORKING_SET infoOriginal = (PCLASS_WORKING_SET)Data;
566         PCLASS_WORKING_SET info = NULL;
567 
568         // only try to allocate memory for cached copy if size is correct
569         if (infoOriginal->Size != sizeof(CLASS_WORKING_SET))
570         {
571             // incorrect size -- client programming error
572             status = STATUS_INVALID_PARAMETER;
573         }
574         else
575         {
576             info = ExAllocatePoolWithTag(NonPagedPoolNx,
577                                          sizeof(CLASS_WORKING_SET),
578                                          CLASS_TAG_WORKING_SET
579                                          );
580             if (info == NULL)
581             {
582                 status = STATUS_INSUFFICIENT_RESOURCES;
583             }
584             else
585             {
586                 // cache the structure internally
587                 RtlCopyMemory(info, infoOriginal, sizeof(CLASS_WORKING_SET));
588                 status = STATUS_SUCCESS;
589             }
590         }
591         // if we successfully cached a copy, validate all the data within
592         if (NT_SUCCESS(status))
593         {
594             if (info->Size != sizeof(CLASS_WORKING_SET))
595             {
596                 // incorrect size -- client programming error
597                 status = STATUS_INVALID_PARAMETER;
598             }
599             else if (info->XferPacketsWorkingSetMaximum > CLASS_WORKING_SET_MAXIMUM)
600             {
601                 // too many requested in the working set
602                 status = STATUS_INVALID_PARAMETER;
603             }
604             else if (info->XferPacketsWorkingSetMinimum > CLASS_WORKING_SET_MAXIMUM)
605             {
606                 // too many requested in the working set
607                 status = STATUS_INVALID_PARAMETER;
608             }
609             else if (driverExtension->InitData.FdoData.DeviceType != FILE_DEVICE_CD_ROM)
610             {
611                 // classpnp developer wants to restrict this code path
612                 // for now to CDROM devices only.
613                 status = STATUS_INVALID_DEVICE_REQUEST;
614             }
615             else if (driverExtension->WorkingSet != NULL)
616             {
617                 // not allowed to change it once it is set for a driver
618                 status = STATUS_INVALID_PARAMETER;
619                 NT_ASSERT(FALSE);
620             }
621         }
622         // save results or cleanup
623         if (NT_SUCCESS(status))
624         {
625             driverExtension->WorkingSet = info; info = NULL;
626         }
627         else
628         {
629             FREE_POOL( info );
630         }
631     }
632     else if (IsEqualGUID(Guid, &ClassGuidSenseInfo2))
633     {
634         PCLASS_INTERPRET_SENSE_INFO2 infoOriginal = (PCLASS_INTERPRET_SENSE_INFO2)Data;
635         PCLASS_INTERPRET_SENSE_INFO2 info = NULL;
636 
637         // only try to allocate memory for cached copy if size is correct
638         if (infoOriginal->Size != sizeof(CLASS_INTERPRET_SENSE_INFO2))
639         {
640             // incorrect size -- client programming error
641             status = STATUS_INVALID_PARAMETER;
642         }
643         else
644         {
645             info = ExAllocatePoolWithTag(NonPagedPoolNx,
646                                          sizeof(CLASS_INTERPRET_SENSE_INFO2),
647                                          CLASS_TAG_SENSE2
648                                          );
649             if (info == NULL)
650             {
651                 status = STATUS_INSUFFICIENT_RESOURCES;
652             }
653             else
654             {
655                 // cache the structure internally
656                 RtlCopyMemory(info, infoOriginal, sizeof(CLASS_INTERPRET_SENSE_INFO2));
657                 status = STATUS_SUCCESS;
658             }
659         }
660 
661         // if we successfully cached a copy, validate all the data within
662         if (NT_SUCCESS(status))
663         {
664             if (info->Size != sizeof(CLASS_INTERPRET_SENSE_INFO2))
665             {
666                 // incorrect size -- client programming error
667                 status = STATUS_INVALID_PARAMETER;
668             }
669             else if (info->HistoryCount > CLASS_INTERPRET_SENSE_INFO2_MAXIMUM_HISTORY_COUNT)
670             {
671                 // incorrect count -- client programming error
672                 status = STATUS_INVALID_PARAMETER;
673             }
674             else if (info->Compress == NULL)
675             {
676                 // Compression of the history is required to be supported
677                 status = STATUS_INVALID_PARAMETER;
678             }
679             else if (info->HistoryCount == 0)
680             {
681                 // History count cannot be zero
682                 status = STATUS_INVALID_PARAMETER;
683             }
684             else if (info->Interpret == NULL)
685             {
686                 // Updated interpret sense info function is required
687                 status = STATUS_INVALID_PARAMETER;
688             }
689             else if (driverExtension->InitData.FdoData.DeviceType != FILE_DEVICE_CD_ROM)
690             {
691                 // classpnp developer wants to restrict this code path
692                 // for now to CDROM devices only.
693                 status = STATUS_INVALID_DEVICE_REQUEST;
694             }
695             else if (driverExtension->InterpretSenseInfo != NULL)
696             {
697                 // not allowed to change it once it is set for a driver
698                 status = STATUS_INVALID_PARAMETER;
699                 NT_ASSERT(FALSE);
700             }
701         }
702 
703         // save results or cleanup
704         if (NT_SUCCESS(status))
705         {
706             driverExtension->InterpretSenseInfo = info; info = NULL;
707         }
708         else
709         {
710             FREE_POOL( info );
711         }
712     }
713     else if (IsEqualGUID(Guid, &ClassGuidSrbSupport))
714     {
715         ULONG srbSupport = *((PULONG)Data);
716 
717         //
718         // Validate that at least one of the supported bit flags is set. Assume
719         // all class drivers support SCSI_REQUEST_BLOCK as a class driver that
720         // supports only extended SRB is not feasible.
721         //
722         if ((srbSupport &
723              (CLASS_SRB_SCSI_REQUEST_BLOCK | CLASS_SRB_STORAGE_REQUEST_BLOCK)) != 0) {
724             driverExtension->SrbSupport = srbSupport;
725             status = STATUS_SUCCESS;
726 
727             //
728             // Catch cases of a class driver reporting only extended SRB support
729             //
730             if ((driverExtension->SrbSupport & CLASS_SRB_SCSI_REQUEST_BLOCK) == 0) {
731                 NT_ASSERT(FALSE);
732             }
733         } else {
734             status = STATUS_INVALID_PARAMETER;
735         }
736     }
737     else
738     {
739         status = STATUS_NOT_SUPPORTED;
740     }
741 
742     return status;
743 
744 } // end ClassInitializeEx()
745 
746 /*++////////////////////////////////////////////////////////////////////////////
747 
748 ClassUnload()
749 
750 Routine Description:
751 
752     called when there are no more references to the driver.  this allows
753     drivers to be updated without rebooting.
754 
755 Arguments:
756 
757     DriverObject - a pointer to the driver object that is being unloaded
758 
759 Status:
760 
761 --*/
762 VOID
763 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassUnload(IN PDRIVER_OBJECT DriverObject)764 ClassUnload(
765     IN PDRIVER_OBJECT DriverObject
766     )
767 {
768     PCLASS_DRIVER_EXTENSION driverExtension;
769 
770     PAGED_CODE();
771 
772     NT_ASSERT( DriverObject->DeviceObject == NULL );
773 
774 #ifdef _MSC_VER
775 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
776 #endif
777     driverExtension = IoGetDriverObjectExtension( DriverObject, CLASS_DRIVER_EXTENSION_KEY );
778 
779 
780     if (driverExtension == NULL)
781     {
782         NT_ASSERT(FALSE);
783         return;
784     }
785 
786     NT_ASSERT(driverExtension->RegistryPath.Buffer != NULL);
787     NT_ASSERT(driverExtension->InitData.ClassUnload != NULL);
788 
789     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,  "ClassUnload: driver unloading %wZ\n",
790                 &driverExtension->RegistryPath));
791 
792     //
793     // attempt to process the driver's unload routine first.
794     //
795 
796     driverExtension->InitData.ClassUnload(DriverObject);
797 
798     //
799     // free own allocated resources and return
800     //
801 
802     FREE_POOL( driverExtension->WorkingSet          );
803     FREE_POOL( driverExtension->InterpretSenseInfo  );
804     FREE_POOL( driverExtension->RegistryPath.Buffer );
805     driverExtension->RegistryPath.Length = 0;
806     driverExtension->RegistryPath.MaximumLength = 0;
807 
808 
809     //
810     // Unregister ETW
811     //
812     if (driverExtension->EtwHandle != 0) {
813         EtwUnregister(driverExtension->EtwHandle);
814         driverExtension->EtwHandle = 0;
815 
816         WPP_CLEANUP(DriverObject);
817     }
818 
819 
820     return;
821 } // end ClassUnload()
822 
823 /*++////////////////////////////////////////////////////////////////////////////
824 
825 ClassAddDevice()
826 
827 Routine Description:
828 
829     SCSI class driver add device routine.  This is called by pnp when a new
830     physical device come into being.
831 
832     This routine will call out to the class driver to verify that it should
833     own this device then will create and attach a device object and then hand
834     it to the driver to initialize and create symbolic links
835 
836 Arguments:
837 
838     DriverObject - a pointer to the driver object that this is being created for
839     PhysicalDeviceObject - a pointer to the physical device object
840 
841 Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
842     STATUS_SUCCESS if the creation and attachment was successful
843     status of device creation and initialization
844 
845 --*/
846 NTSTATUS
847 #ifdef _MSC_VER
848 #pragma prefast(suppress:28152, "We expect the class driver to clear the DO_DEVICE_INITIALIZING flag in its AddDevice routine.")
849 #endif
850 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassAddDevice(IN PDRIVER_OBJECT DriverObject,IN PDEVICE_OBJECT PhysicalDeviceObject)851 ClassAddDevice(
852     IN PDRIVER_OBJECT DriverObject,
853     IN PDEVICE_OBJECT PhysicalDeviceObject
854     )
855 {
856 #ifdef _MSC_VER
857 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
858 #endif
859     PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
860 
861     NTSTATUS status;
862 
863     PAGED_CODE();
864 
865 
866     status = driverExtension->InitData.ClassAddDevice(DriverObject,
867                                               PhysicalDeviceObject);
868 
869     return status;
870 } // end ClassAddDevice()
871 
872 /*++////////////////////////////////////////////////////////////////////////////
873 
874 ClassDispatchPnp()
875 
876 Routine Description:
877 
878     Storage class driver pnp routine.  This is called by the io system when
879     a PNP request is sent to the device.
880 
881 Arguments:
882 
883     DeviceObject - pointer to the device object
884 
885     Irp - pointer to the io request packet
886 
887 Return Value:
888 
889     status
890 
891 --*/
892 NTSTATUS
893 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassDispatchPnp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)894 ClassDispatchPnp(
895     IN PDEVICE_OBJECT DeviceObject,
896     IN PIRP Irp
897     )
898 {
899     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
900     BOOLEAN isFdo = commonExtension->IsFdo;
901 
902     PCLASS_DRIVER_EXTENSION driverExtension;
903     PCLASS_INIT_DATA initData;
904     PCLASS_DEV_INFO devInfo;
905 
906     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
907 
908     NTSTATUS status = Irp->IoStatus.Status;
909     BOOLEAN completeRequest = TRUE;
910     BOOLEAN lockReleased = FALSE;
911 
912 
913     PAGED_CODE();
914 
915     //
916     // Extract all the useful information out of the driver object
917     // extension
918     //
919 
920 #ifdef _MSC_VER
921 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
922 #endif
923     driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
924 
925     if (driverExtension){
926 
927         initData = &(driverExtension->InitData);
928 
929         if(isFdo) {
930             devInfo = &(initData->FdoData);
931         } else {
932             devInfo = &(initData->PdoData);
933         }
934 
935         ClassAcquireRemoveLock(DeviceObject, Irp);
936 
937         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
938                        DeviceObject, Irp,
939                        irpStack->MinorFunction,
940                        isFdo ? "fdo" : "pdo",
941                        DeviceObject));
942         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
943                        DeviceObject, Irp,
944                        commonExtension->PreviousState,
945                        commonExtension->CurrentState));
946 
947 
948         switch(irpStack->MinorFunction) {
949 
950             case IRP_MN_START_DEVICE: {
951 
952                 //
953                 // if this is sent to the FDO we should forward it down the
954                 // attachment chain before we start the FDO.
955                 //
956 
957                 if (isFdo) {
958                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
959                 }
960                 else {
961                     status = STATUS_SUCCESS;
962                 }
963 
964                 if (NT_SUCCESS(status)){
965                     status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
966                 }
967 
968                 break;
969             }
970 
971 
972             case IRP_MN_QUERY_DEVICE_RELATIONS: {
973 
974                 DEVICE_RELATION_TYPE type =
975                     irpStack->Parameters.QueryDeviceRelations.Type;
976 
977                 PDEVICE_RELATIONS deviceRelations = NULL;
978 
979 
980                 if(!isFdo) {
981 
982                     if(type == TargetDeviceRelation) {
983 
984                         //
985                         // Device relations has one entry built in to it's size.
986                         //
987 
988                         status = STATUS_INSUFFICIENT_RESOURCES;
989 
990                         deviceRelations = ExAllocatePoolWithTag(PagedPool,
991                                                          sizeof(DEVICE_RELATIONS),
992                                                          '2CcS');
993 
994                         if(deviceRelations != NULL) {
995 
996                             RtlZeroMemory(deviceRelations,
997                                           sizeof(DEVICE_RELATIONS));
998 
999                             Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
1000 
1001                             deviceRelations->Count = 1;
1002                             deviceRelations->Objects[0] = DeviceObject;
1003                             ObReferenceObject(deviceRelations->Objects[0]);
1004 
1005                             status = STATUS_SUCCESS;
1006                         }
1007 
1008                     } else {
1009                         //
1010                         // PDO's just complete enumeration requests without altering
1011                         // the status.
1012                         //
1013 
1014                         status = Irp->IoStatus.Status;
1015                     }
1016 
1017                     break;
1018 
1019                 } else if (type == BusRelations) {
1020 
1021                     NT_ASSERT(commonExtension->IsInitialized);
1022 
1023                     //
1024                     // Make sure we support enumeration
1025                     //
1026 
1027                     if(initData->ClassEnumerateDevice == NULL) {
1028 
1029                         //
1030                         // Just send the request down to the lower driver.  Perhaps
1031                         // It can enumerate children.
1032                         //
1033 
1034                     } else {
1035 
1036                         //
1037                         // Re-enumerate the device
1038                         //
1039 
1040                         status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
1041 
1042                         if(!NT_SUCCESS(status)) {
1043                             completeRequest = TRUE;
1044                             break;
1045                         }
1046                     }
1047                 }
1048 
1049 
1050                 IoCopyCurrentIrpStackLocationToNext(Irp);
1051                 ClassReleaseRemoveLock(DeviceObject, Irp);
1052                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1053                 completeRequest = FALSE;
1054 
1055                 break;
1056             }
1057 
1058             case IRP_MN_QUERY_ID: {
1059 
1060                 BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
1061                 UNICODE_STRING unicodeString;
1062 
1063 
1064                 if(isFdo) {
1065 
1066 
1067                     //
1068                     // FDO's should just forward the query down to the lower
1069                     // device objects
1070                     //
1071 
1072                     IoCopyCurrentIrpStackLocationToNext(Irp);
1073                     ClassReleaseRemoveLock(DeviceObject, Irp);
1074 
1075                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1076                     completeRequest = FALSE;
1077                     break;
1078                 }
1079 
1080                 //
1081                 // PDO's need to give an answer - this is easy for now
1082                 //
1083 
1084                 RtlInitUnicodeString(&unicodeString, NULL);
1085 
1086                 status = ClassGetPdoId(DeviceObject,
1087                                        idType,
1088                                        &unicodeString);
1089 
1090                 if(status == STATUS_NOT_IMPLEMENTED) {
1091                     //
1092                     // The driver doesn't implement this ID (whatever it is).
1093                     // Use the status out of the IRP so that we don't mangle a
1094                     // response from someone else.
1095                     //
1096 
1097                     status = Irp->IoStatus.Status;
1098                 } else if(NT_SUCCESS(status)) {
1099                     Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
1100                 } else {
1101                     Irp->IoStatus.Information = (ULONG_PTR) NULL;
1102                 }
1103 
1104                 break;
1105             }
1106 
1107             case IRP_MN_QUERY_STOP_DEVICE:
1108             case IRP_MN_QUERY_REMOVE_DEVICE: {
1109 
1110 
1111                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
1112                             DeviceObject, Irp,
1113                             ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
1114                              "STOP" : "REMOVE")));
1115 
1116                 //
1117                 // If this device is in use for some reason (paging, etc...)
1118                 // then we need to fail the request.
1119                 //
1120 
1121                 if(commonExtension->PagingPathCount != 0) {
1122 
1123                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): device is in paging "
1124                                 "path and cannot be removed\n",
1125                                 DeviceObject, Irp));
1126                     status = STATUS_DEVICE_BUSY;
1127                     break;
1128                 }
1129 
1130 
1131                 //
1132                 // Check with the class driver to see if the query operation
1133                 // can succeed.
1134                 //
1135 
1136                 if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
1137                     status = devInfo->ClassStopDevice(DeviceObject,
1138                                                       irpStack->MinorFunction);
1139                 } else {
1140                     status = devInfo->ClassRemoveDevice(DeviceObject,
1141                                                         irpStack->MinorFunction);
1142                 }
1143 
1144                 if(NT_SUCCESS(status)) {
1145 
1146                     //
1147                     // ASSERT that we never get two queries in a row, as
1148                     // this will severly mess up the state machine
1149                     //
1150                     NT_ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
1151                     commonExtension->PreviousState = commonExtension->CurrentState;
1152                     commonExtension->CurrentState = irpStack->MinorFunction;
1153 
1154                     if(isFdo) {
1155                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
1156                                     "%s irp\n", DeviceObject, Irp,
1157                                     ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
1158                                      "STOP" : "REMOVE")));
1159                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1160                     }
1161                 }
1162 
1163 
1164                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Final status == %x\n",
1165                             DeviceObject, Irp, status));
1166 
1167                 break;
1168             }
1169 
1170             case IRP_MN_CANCEL_STOP_DEVICE:
1171             case IRP_MN_CANCEL_REMOVE_DEVICE: {
1172 
1173 
1174                 //
1175                 // Check with the class driver to see if the query or cancel
1176                 // operation can succeed.
1177                 //
1178 
1179                 if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
1180                     status = devInfo->ClassStopDevice(DeviceObject,
1181                                                       irpStack->MinorFunction);
1182                     NT_ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should never be failed\n", NT_SUCCESS(status));
1183                 } else {
1184                     status = devInfo->ClassRemoveDevice(DeviceObject,
1185                                                         irpStack->MinorFunction);
1186                     NT_ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should never be failed\n", NT_SUCCESS(status));
1187                 }
1188 
1189                 Irp->IoStatus.Status = status;
1190 
1191                 //
1192                 // We got a CANCEL - roll back to the previous state only
1193                 // if the current state is the respective QUERY state.
1194                 //
1195 
1196                 if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
1197                     (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
1198                     ) ||
1199                    ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
1200                     (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
1201                     )
1202                    ) {
1203 
1204                     commonExtension->CurrentState =
1205                         commonExtension->PreviousState;
1206                     commonExtension->PreviousState = 0xff;
1207 
1208                 }
1209 
1210 
1211                 if(isFdo) {
1212 
1213 
1214                     IoCopyCurrentIrpStackLocationToNext(Irp);
1215                     ClassReleaseRemoveLock(DeviceObject, Irp);
1216                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1217                     completeRequest = FALSE;
1218                 } else {
1219                     status = STATUS_SUCCESS;
1220                 }
1221 
1222                 break;
1223             }
1224 
1225             case IRP_MN_STOP_DEVICE: {
1226 
1227 
1228                 //
1229                 // These all mean nothing to the class driver currently.  The
1230                 // port driver will handle all queueing when necessary.
1231                 //
1232 
1233                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): got stop request for %s\n",
1234                             DeviceObject, Irp,
1235                             (isFdo ? "fdo" : "pdo")
1236                             ));
1237 
1238                 NT_ASSERT(commonExtension->PagingPathCount == 0);
1239 
1240                 //
1241                 // ISSUE-2000/02/03-peterwie
1242                 // if we stop the timer here then it means no class driver can
1243                 // do i/o in its ClassStopDevice routine.  This is because the
1244                 // retry (among other things) is tied into the tick handler
1245                 // and disabling retries could cause the class driver to deadlock.
1246                 // Currently no class driver we're aware of issues i/o in its
1247                 // Stop routine but this is a case we may want to defend ourself
1248                 // against.
1249                 //
1250 
1251                 ClasspDisableTimer((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension);
1252 
1253 
1254                 status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
1255 
1256                 NT_ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should never be failed\n", NT_SUCCESS(status));
1257 
1258                 if(isFdo) {
1259                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
1260                 }
1261 
1262                 if(NT_SUCCESS(status)) {
1263                     commonExtension->CurrentState = irpStack->MinorFunction;
1264                     commonExtension->PreviousState = 0xff;
1265                 }
1266 
1267 
1268                 break;
1269             }
1270 
1271             case IRP_MN_REMOVE_DEVICE:
1272             case IRP_MN_SURPRISE_REMOVAL: {
1273                 UCHAR removeType = irpStack->MinorFunction;
1274 
1275                 //
1276                 // Log a sytem event when non-removable disks are surprise-removed.
1277                 //
1278                 if (isFdo &&
1279                     (removeType == IRP_MN_SURPRISE_REMOVAL)) {
1280 
1281                     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1282                     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
1283                     BOOLEAN logSurpriseRemove = TRUE;
1284                     STORAGE_BUS_TYPE busType = fdoExtension->DeviceDescriptor->BusType;
1285 
1286                     //
1287                     // Don't log an event for VHDs
1288                     //
1289                     if (busType == BusTypeFileBackedVirtual) {
1290                         logSurpriseRemove = FALSE;
1291 
1292                     } else if (fdoData->HotplugInfo.MediaRemovable) {
1293                         logSurpriseRemove = FALSE;
1294 
1295                     } else if (fdoData->HotplugInfo.DeviceHotplug && ( busType == BusTypeUsb || busType == BusType1394)) {
1296 
1297                         /*
1298                         This device is reported as DeviceHotplug but since the busType is Usb or 1394, don't log an event
1299                         Note that some storage arrays may report DeviceHotplug and we would like to log an event in those cases
1300                         */
1301 
1302                         logSurpriseRemove = FALSE;
1303                     }
1304 
1305                     if (logSurpriseRemove) {
1306 
1307                         ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_WARNING_DISK_SURPRISE_REMOVED);
1308                     }
1309 
1310                 }
1311 
1312                 if (commonExtension->PagingPathCount != 0) {
1313                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
1314                 }
1315 
1316                 //
1317                 // Release the lock for this IRP before calling in.
1318                 //
1319                 ClassReleaseRemoveLock(DeviceObject, Irp);
1320                 lockReleased = TRUE;
1321 
1322                 /*
1323                  *  Set IsRemoved before propagating the REMOVE down the stack.
1324                  *  This keeps class-initiated I/O (e.g. the MCN irp) from getting sent
1325                  *  after we propagate the remove.
1326                  */
1327                 commonExtension->IsRemoved = REMOVE_PENDING;
1328 
1329                 /*
1330                  *  If a timer was started on the device, stop it.
1331                  */
1332                  ClasspDisableTimer((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension);
1333 
1334                 /*
1335                  *  "Fire-and-forget" the remove irp to the lower stack.
1336                  *  Don't touch the irp (or the irp stack!) after this.
1337                  */
1338                 if (isFdo) {
1339 
1340 
1341                     IoCopyCurrentIrpStackLocationToNext(Irp);
1342                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1343                     NT_ASSERT(NT_SUCCESS(status));
1344                     completeRequest = FALSE;
1345                 }
1346                 else {
1347                     status = STATUS_SUCCESS;
1348                 }
1349 
1350                 /*
1351                  *  Do our own cleanup and call the class driver's remove
1352                  *  cleanup routine.
1353                  *  For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
1354                  *  so don't touch the extension after this.
1355                  */
1356                 commonExtension->PreviousState = commonExtension->CurrentState;
1357                 commonExtension->CurrentState = removeType;
1358                 ClassRemoveDevice(DeviceObject, removeType);
1359 
1360                 break;
1361             }
1362 
1363             case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
1364 
1365                 DEVICE_USAGE_NOTIFICATION_TYPE type = irpStack->Parameters.UsageNotification.Type;
1366                 BOOLEAN setPagable;
1367 
1368 
1369                 switch(type) {
1370 
1371                     case DeviceUsageTypePaging: {
1372 
1373                         if ((irpStack->Parameters.UsageNotification.InPath) &&
1374                             (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
1375 
1376                             //
1377                             // Device isn't started.  Don't allow adding a
1378                             // paging file, but allow a removal of one.
1379                             //
1380 
1381                             status = STATUS_DEVICE_NOT_READY;
1382                             break;
1383                         }
1384 
1385                         NT_ASSERT(commonExtension->IsInitialized);
1386 
1387                         /*
1388                          *  Ensure that this user thread is not suspended while we are holding the PathCountEvent.
1389                          */
1390                         KeEnterCriticalRegion();
1391 
1392                         (VOID)KeWaitForSingleObject(&commonExtension->PathCountEvent,
1393                                                     Executive, KernelMode,
1394                                                     FALSE, NULL);
1395                         status = STATUS_SUCCESS;
1396 
1397                         //
1398                         // If the volume is removable we should try to lock it in
1399                         // place or unlock it once per paging path count
1400                         //
1401 
1402                         if (commonExtension->IsFdo){
1403                             status = ClasspEjectionControl(
1404                                             DeviceObject,
1405                                             Irp,
1406                                             InternalMediaLock,
1407                                             (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
1408                         }
1409 
1410                         if (!NT_SUCCESS(status)){
1411                             KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
1412                             KeLeaveCriticalRegion();
1413                             break;
1414                         }
1415 
1416                         //
1417                         // if removing last paging device, need to set DO_POWER_PAGABLE
1418                         // bit here, and possible re-set it below on failure.
1419                         //
1420 
1421                         setPagable = FALSE;
1422 
1423                         if ((!irpStack->Parameters.UsageNotification.InPath) &&
1424                             (commonExtension->PagingPathCount == 1)) {
1425 
1426                             //
1427                             // removing last paging file
1428                             // must have DO_POWER_PAGABLE bits set, but only
1429                             // if none set the DO_POWER_INRUSH bit and no other special files
1430                             //
1431 
1432                             if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1433                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Last "
1434                                             "paging file removed, but "
1435                                             "DO_POWER_INRUSH was set, so NOT "
1436                                             "setting DO_POWER_PAGABLE\n",
1437                                             DeviceObject, Irp));
1438                             } else if ((commonExtension->HibernationPathCount == 0) &&
1439                                        (commonExtension->DumpPathCount == 0)) {
1440                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Last "
1441                                             "paging file removed, "
1442                                             "setting DO_POWER_PAGABLE\n",
1443                                             DeviceObject, Irp));
1444                                 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1445                                 setPagable = TRUE;
1446                             }
1447 
1448                         }
1449 
1450                         //
1451                         // forward the irp before finishing handling the
1452                         // special cases
1453                         //
1454 
1455                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1456 
1457                         //
1458                         // now deal with the failure and success cases.
1459                         // note that we are not allowed to fail the irp
1460                         // once it is sent to the lower drivers.
1461                         //
1462 
1463                         if (NT_SUCCESS(status)) {
1464 
1465                             IoAdjustPagingPathCount(
1466                                 (volatile LONG *)&commonExtension->PagingPathCount,
1467                                 irpStack->Parameters.UsageNotification.InPath);
1468 
1469                             if (irpStack->Parameters.UsageNotification.InPath) {
1470                                 if (commonExtension->PagingPathCount == 1) {
1471                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): "
1472                                                 "Clearing PAGABLE bit\n",
1473                                                 DeviceObject, Irp));
1474                                     CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1475 
1476 
1477                                 }
1478 
1479                             }
1480 
1481                         } else {
1482 
1483                             //
1484                             // cleanup the changes done above
1485                             //
1486 
1487                             if (setPagable == TRUE) {
1488                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Unsetting "
1489                                             "PAGABLE bit due to irp failure\n",
1490                                             DeviceObject, Irp));
1491                                 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1492                                 setPagable = FALSE;
1493                             }
1494 
1495                             //
1496                             // relock or unlock the media if needed.
1497                             //
1498 
1499                             if (commonExtension->IsFdo) {
1500 
1501                                 ClasspEjectionControl(
1502                                         DeviceObject,
1503                                         Irp,
1504                                         InternalMediaLock,
1505                                         (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
1506                             }
1507                         }
1508 
1509                         //
1510                         // set the event so the next one can occur.
1511                         //
1512 
1513                         KeSetEvent(&commonExtension->PathCountEvent,
1514                                    IO_NO_INCREMENT, FALSE);
1515                         KeLeaveCriticalRegion();
1516                         break;
1517                     }
1518 
1519                     case DeviceUsageTypeHibernation: {
1520 
1521                         //
1522                         // if removing last hiber device, need to set DO_POWER_PAGABLE
1523                         // bit here, and possible re-set it below on failure.
1524                         //
1525 
1526                         setPagable = FALSE;
1527 
1528                         if ((!irpStack->Parameters.UsageNotification.InPath) &&
1529                             (commonExtension->HibernationPathCount == 1)) {
1530 
1531                             //
1532                             // removing last hiber file
1533                             // must have DO_POWER_PAGABLE bits set, but only
1534                             // if none set the DO_POWER_INRUSH bit and no other special files
1535                             //
1536 
1537                             if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1538                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Last "
1539                                             "hiber file removed, but "
1540                                             "DO_POWER_INRUSH was set, so NOT "
1541                                             "setting DO_POWER_PAGABLE\n",
1542                                             DeviceObject, Irp));
1543                             } else if ((commonExtension->PagingPathCount == 0) &&
1544                                        (commonExtension->DumpPathCount == 0)) {
1545                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Last "
1546                                             "hiber file removed, "
1547                                             "setting DO_POWER_PAGABLE\n",
1548                                             DeviceObject, Irp));
1549                                 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1550                                 setPagable = TRUE;
1551                             }
1552 
1553                         }
1554 
1555                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1556                         if (!NT_SUCCESS(status)) {
1557 
1558                             if (setPagable == TRUE) {
1559                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Unsetting "
1560                                             "PAGABLE bit due to irp failure\n",
1561                                             DeviceObject, Irp));
1562                                 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1563                                 setPagable = FALSE;
1564                             }
1565 
1566                         } else {
1567 
1568                             IoAdjustPagingPathCount(
1569                                 (volatile LONG *)&commonExtension->HibernationPathCount,
1570                                 irpStack->Parameters.UsageNotification.InPath
1571                                 );
1572 
1573                             if ((irpStack->Parameters.UsageNotification.InPath) &&
1574                                 (commonExtension->HibernationPathCount == 1)) {
1575                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): "
1576                                             "Clearing PAGABLE bit\n",
1577                                             DeviceObject, Irp));
1578                                 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1579                             }
1580                         }
1581 
1582                         break;
1583                     }
1584 
1585                     case DeviceUsageTypeDumpFile: {
1586 
1587                         //
1588                         // if removing last dump device, need to set DO_POWER_PAGABLE
1589                         // bit here, and possible re-set it below on failure.
1590                         //
1591 
1592                         setPagable = FALSE;
1593 
1594                         if ((!irpStack->Parameters.UsageNotification.InPath) &&
1595                             (commonExtension->DumpPathCount == 1)) {
1596 
1597                             //
1598                             // removing last dump file
1599                             // must have DO_POWER_PAGABLE bits set, but only
1600                             // if none set the DO_POWER_INRUSH bit and no other special files
1601                             //
1602 
1603                             if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
1604                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Last "
1605                                             "dump file removed, but "
1606                                             "DO_POWER_INRUSH was set, so NOT "
1607                                             "setting DO_POWER_PAGABLE\n",
1608                                             DeviceObject, Irp));
1609                             } else if ((commonExtension->PagingPathCount == 0) &&
1610                                        (commonExtension->HibernationPathCount == 0)) {
1611                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Last "
1612                                             "dump file removed, "
1613                                             "setting DO_POWER_PAGABLE\n",
1614                                             DeviceObject, Irp));
1615                                 SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1616                                 setPagable = TRUE;
1617                             }
1618 
1619                         }
1620 
1621                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1622                         if (!NT_SUCCESS(status)) {
1623 
1624                             if (setPagable == TRUE) {
1625                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): Unsetting "
1626                                             "PAGABLE bit due to irp failure\n",
1627                                             DeviceObject, Irp));
1628                                 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1629                                 setPagable = FALSE;
1630                             }
1631 
1632                         } else {
1633 
1634                             IoAdjustPagingPathCount(
1635                                 (volatile LONG *)&commonExtension->DumpPathCount,
1636                                 irpStack->Parameters.UsageNotification.InPath
1637                                 );
1638 
1639                             if ((irpStack->Parameters.UsageNotification.InPath) &&
1640                                 (commonExtension->DumpPathCount == 1)) {
1641                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): "
1642                                             "Clearing PAGABLE bit\n",
1643                                             DeviceObject, Irp));
1644                                 CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
1645                             }
1646                         }
1647 
1648                         break;
1649                     }
1650 
1651                     case DeviceUsageTypeBoot: {
1652 
1653                         if (isFdo) {
1654                             PCLASS_PRIVATE_FDO_DATA fdoData;
1655 
1656                             fdoData = ((PFUNCTIONAL_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->PrivateFdoData;
1657 
1658 
1659                             //
1660                             // If boot disk has removal policy as RemovalPolicyExpectSurpriseRemoval (e.g. disk is hotplug-able),
1661                             // change the removal policy to RemovalPolicyExpectOrderlyRemoval.
1662                             // This will cause the write cache of disk to be enabled on subsequent start Fdo (next boot).
1663                             //
1664                             if ((fdoData != NULL) &&
1665                                 fdoData->HotplugInfo.DeviceHotplug) {
1666 
1667                                 fdoData->HotplugInfo.DeviceHotplug = FALSE;
1668                                 fdoData->HotplugInfo.MediaRemovable = FALSE;
1669 
1670                                 ClassSetDeviceParameter((PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension,
1671                                                         CLASSP_REG_SUBKEY_NAME,
1672                                                         CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
1673                                                         RemovalPolicyExpectOrderlyRemoval);
1674                             }
1675                         }
1676 
1677                         status = ClassForwardIrpSynchronous(commonExtension, Irp);
1678                         break;
1679                     }
1680 
1681                     default: {
1682                         status = STATUS_INVALID_PARAMETER;
1683                         break;
1684                     }
1685                 }
1686                 break;
1687             }
1688 
1689             case IRP_MN_QUERY_CAPABILITIES: {
1690 
1691                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
1692                             DeviceObject, Irp));
1693 
1694                 if(!isFdo) {
1695 
1696                     status = ClassQueryPnpCapabilities(
1697                                 DeviceObject,
1698                                 irpStack->Parameters.DeviceCapabilities.Capabilities
1699                                 );
1700 
1701                     break;
1702 
1703                 } else {
1704 
1705                     PDEVICE_CAPABILITIES deviceCapabilities;
1706                     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1707                     PCLASS_PRIVATE_FDO_DATA fdoData;
1708 
1709                     fdoExtension = DeviceObject->DeviceExtension;
1710                     fdoData = fdoExtension->PrivateFdoData;
1711                     deviceCapabilities =
1712                         irpStack->Parameters.DeviceCapabilities.Capabilities;
1713 
1714                     //
1715                     // forward the irp before handling the special cases
1716                     //
1717 
1718                     status = ClassForwardIrpSynchronous(commonExtension, Irp);
1719                     if (!NT_SUCCESS(status)) {
1720                         break;
1721                     }
1722 
1723                     //
1724                     // we generally want to remove the device from the hotplug
1725                     // applet, which requires the SR-OK bit to be set.
1726                     // only when the user specifies that they are capable of
1727                     // safely removing things do we want to clear this bit
1728                     // (saved in WriteCacheEnableOverride)
1729                     //
1730                     // setting of this bit is done either above, or by the
1731                     // lower driver.
1732                     //
1733                     // note: may not be started, so check we have FDO data first.
1734                     //
1735 
1736                     if (fdoData &&
1737                         fdoData->HotplugInfo.WriteCacheEnableOverride) {
1738                         if (deviceCapabilities->SurpriseRemovalOK) {
1739                             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,  "Classpnp: Clearing SR-OK bit in "
1740                                         "device capabilities due to hotplug "
1741                                         "device or media\n"));
1742                         }
1743                         deviceCapabilities->SurpriseRemovalOK = FALSE;
1744                     }
1745                     break;
1746 
1747                 } // end QUERY_CAPABILITIES for FDOs
1748 
1749                 break;
1750 
1751 
1752             } // end QUERY_CAPABILITIES
1753 
1754             default: {
1755 
1756                 if (isFdo){
1757 
1758 
1759                     IoCopyCurrentIrpStackLocationToNext(Irp);
1760 
1761                     ClassReleaseRemoveLock(DeviceObject, Irp);
1762                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1763 
1764                     completeRequest = FALSE;
1765                 }
1766 
1767                 break;
1768             }
1769         }
1770     }
1771     else {
1772         NT_ASSERT(driverExtension);
1773         status = STATUS_INTERNAL_ERROR;
1774     }
1775 
1776     if (completeRequest){
1777         Irp->IoStatus.Status = status;
1778 
1779         if (!lockReleased){
1780             ClassReleaseRemoveLock(DeviceObject, Irp);
1781         }
1782 
1783         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1784 
1785         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
1786     }
1787     else {
1788         /*
1789          *  The irp is already completed so don't touch it.
1790          *  This may be a remove so don't touch the device extension.
1791          */
1792         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
1793     }
1794 
1795     return status;
1796 } // end ClassDispatchPnp()
1797 
1798 
1799 /*++////////////////////////////////////////////////////////////////////////////
1800 
1801 ClassPnpStartDevice()
1802 
1803 Routine Description:
1804 
1805     Storage class driver routine for IRP_MN_START_DEVICE requests.
1806     This routine kicks off any device specific initialization
1807 
1808 Arguments:
1809 
1810     DeviceObject - a pointer to the device object
1811 
1812     Irp - a pointer to the io request packet
1813 
1814 Return Value:
1815 
1816     none
1817 
1818 --*/
ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)1819 NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
1820 {
1821     PCLASS_DRIVER_EXTENSION driverExtension;
1822     PCLASS_INIT_DATA initData;
1823 
1824     PCLASS_DEV_INFO devInfo;
1825 
1826     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1827     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1828     BOOLEAN isFdo = commonExtension->IsFdo;
1829 
1830     BOOLEAN isMountedDevice = TRUE;
1831     BOOLEAN isPortable = FALSE;
1832 
1833     NTSTATUS status = STATUS_SUCCESS;
1834     PDEVICE_POWER_DESCRIPTOR powerDescriptor = NULL;
1835 
1836 
1837     PAGED_CODE();
1838 
1839 #ifdef _MSC_VER
1840 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
1841 #endif
1842     driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
1843 
1844     initData = &(driverExtension->InitData);
1845     if(isFdo) {
1846         devInfo = &(initData->FdoData);
1847     } else {
1848         devInfo = &(initData->PdoData);
1849     }
1850 
1851     NT_ASSERT(devInfo->ClassInitDevice != NULL);
1852     NT_ASSERT(devInfo->ClassStartDevice != NULL);
1853 
1854     if (!commonExtension->IsInitialized){
1855 
1856         //
1857         // perform FDO/PDO specific initialization
1858         //
1859 
1860         if (isFdo){
1861             STORAGE_PROPERTY_ID propertyId;
1862 
1863             //
1864             // allocate a private extension for class data
1865             //
1866 
1867             if (fdoExtension->PrivateFdoData == NULL) {
1868                 fdoExtension->PrivateFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
1869                                                                      sizeof(CLASS_PRIVATE_FDO_DATA),
1870                                                                      CLASS_TAG_PRIVATE_DATA
1871                                                                      );
1872             }
1873 
1874             if (fdoExtension->PrivateFdoData == NULL) {
1875                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "ClassPnpStartDevice: Cannot allocate for private fdo data\n"));
1876                 return STATUS_INSUFFICIENT_RESOURCES;
1877             }
1878 
1879             RtlZeroMemory(fdoExtension->PrivateFdoData, sizeof(CLASS_PRIVATE_FDO_DATA));
1880 
1881 
1882 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
1883             //
1884             // Allocate a structure to hold more data than what we can put in FUNCTIONAL_DEVICE_EXTENSION.
1885             // This structure's memory is managed by classpnp, so it is more extensible.
1886             //
1887             if (fdoExtension->AdditionalFdoData == NULL) {
1888                 fdoExtension->AdditionalFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
1889                                                                         sizeof(ADDITIONAL_FDO_DATA),
1890                                                                         CLASSPNP_POOL_TAG_ADDITIONAL_DATA
1891                                                                         );
1892             }
1893 
1894             if (fdoExtension->AdditionalFdoData == NULL) {
1895                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate memory for the additional data structure.\n"));
1896                 return STATUS_INSUFFICIENT_RESOURCES;
1897             }
1898 
1899             RtlZeroMemory(fdoExtension->AdditionalFdoData, sizeof(ADDITIONAL_FDO_DATA));
1900 #endif
1901 
1902             status = ClasspInitializeTimer(fdoExtension);
1903             if (NT_SUCCESS(status) == FALSE) {
1904                 FREE_POOL(fdoExtension->PrivateFdoData);
1905 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
1906                 FREE_POOL(fdoExtension->AdditionalFdoData);
1907 #endif
1908                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Failed to initialize tick timer\n"));
1909                 return STATUS_INSUFFICIENT_RESOURCES;
1910             }
1911 
1912             //
1913             // allocate LowerLayerSupport for class data
1914             //
1915 
1916             if (fdoExtension->FunctionSupportInfo == NULL) {
1917                 fdoExtension->FunctionSupportInfo = (PCLASS_FUNCTION_SUPPORT_INFO)ExAllocatePoolWithTag(NonPagedPoolNx,
1918                                                                                                         sizeof(CLASS_FUNCTION_SUPPORT_INFO),
1919                                                                                                         '3BcS'
1920                                                                                                         );
1921             }
1922 
1923             if (fdoExtension->FunctionSupportInfo == NULL) {
1924                 ClasspDeleteTimer(fdoExtension);
1925                 FREE_POOL(fdoExtension->PrivateFdoData);
1926 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
1927                 FREE_POOL(fdoExtension->AdditionalFdoData);
1928 #endif
1929                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "ClassPnpStartDevice: Cannot allocate for FunctionSupportInfo\n"));
1930                 return STATUS_INSUFFICIENT_RESOURCES;
1931             }
1932 
1933             //
1934             // initialize the struct's various fields.
1935             //
1936             RtlZeroMemory(fdoExtension->FunctionSupportInfo, sizeof(CLASS_FUNCTION_SUPPORT_INFO));
1937             KeInitializeSpinLock(&fdoExtension->FunctionSupportInfo->SyncLock);
1938 
1939             //
1940             // intialize the CommandStatus to -1 indicates that no effort made yet to retrieve the info.
1941             // Possible values of CommandStatus (data type: NTSTATUS):
1942             //     -1:             It's not attempted yet to retrieve the information.
1943             //     success:        Command sent and succeeded, information cached in FdoExtension.
1944             //     failed/warning: Command is either not supported or failed by device or lower level driver.
1945             //                     The command should not be attempted again.
1946             //
1947             fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus = -1;
1948             fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus = -1;
1949             fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = -1;
1950             fdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = -1;
1951             fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = -1;
1952 
1953 
1954             KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
1955             KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
1956                             ClasspRetryRequestDpc,
1957                             DeviceObject);
1958             KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
1959             fdoExtension->PrivateFdoData->Retry.Granularity = KeQueryTimeIncrement();
1960             commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
1961             InitializeListHead(&fdoExtension->PrivateFdoData->DeferredClientIrpList);
1962 
1963             KeInitializeSpinLock(&fdoExtension->PrivateFdoData->SpinLock);
1964 
1965             //
1966             // keep a pointer to the senseinfo2 stuff locally also (used in every read/write).
1967             //
1968             fdoExtension->PrivateFdoData->InterpretSenseInfo = driverExtension->InterpretSenseInfo;
1969 
1970             fdoExtension->PrivateFdoData->MaxNumberOfIoRetries = NUM_IO_RETRIES;
1971 
1972             //
1973             // Initialize release queue extended SRB
1974             //
1975             status = InitializeStorageRequestBlock(&(fdoExtension->PrivateFdoData->ReleaseQueueSrb.SrbEx),
1976                                                    STORAGE_ADDRESS_TYPE_BTL8,
1977                                                    sizeof(fdoExtension->PrivateFdoData->ReleaseQueueSrb.ReleaseQueueSrbBuffer),
1978                                                    0);
1979             if (!NT_SUCCESS(status)) {
1980                 NT_ASSERT(FALSE);
1981                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,
1982                             "ClassPnpStartDevice: fail to initialize release queue extended SRB 0x%x\n", status));
1983                 return status;
1984             }
1985 
1986 
1987             /*
1988              *  Anchor the FDO in our static list.
1989              *  Pnp is synchronized, so we shouldn't need any synchronization here.
1990              */
1991             InsertTailList(&AllFdosList, &fdoExtension->PrivateFdoData->AllFdosListEntry);
1992 
1993             //
1994             // NOTE: the old interface allowed the class driver to allocate
1995             // this.  this was unsafe for low-memory conditions. allocate one
1996             // unconditionally now, and modify our internal functions to use
1997             // our own exclusively as it is the only safe way to do this.
1998             //
1999 
2000             status = ClasspAllocateReleaseQueueIrp(fdoExtension);
2001             if (!NT_SUCCESS(status)) {
2002                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "ClassPnpStartDevice: Cannot allocate the private release queue irp\n"));
2003                 return status;
2004             }
2005 
2006             status = ClasspAllocatePowerProcessIrp(fdoExtension);
2007             if (!NT_SUCCESS(status)) {
2008                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "ClassPnpStartDevice: Cannot allocate the power process irp\n"));
2009                 return status;
2010             }
2011 
2012             //
2013             // Call port driver to get miniport properties for disk devices
2014             // It's ok for this call to fail
2015             //
2016 
2017             if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
2018                 (!TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
2019 
2020                 propertyId = StorageMiniportProperty;
2021 
2022                 status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
2023                                             &propertyId,
2024                                             (PVOID *)&fdoExtension->MiniportDescriptor);
2025 
2026                 //
2027                 // function ClassGetDescriptor returns succeed with buffer "fdoExtension->MiniportDescriptor" allocated.
2028                 //
2029                 if ( NT_SUCCESS(status) &&
2030                      (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport &&
2031                       fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetUSBport) ) {
2032                     //
2033                     // field "IoTimeoutValue" supported for either Storport or USBStor
2034                     //
2035                     fdoExtension->MiniportDescriptor->IoTimeoutValue = 0;
2036                 }
2037 
2038 
2039 
2040             }
2041 
2042             //
2043             // Call port driver to get adapter capabilities.
2044             //
2045 
2046             propertyId = StorageAdapterProperty;
2047 
2048             status = ClassGetDescriptor(
2049                         commonExtension->LowerDeviceObject,
2050                         &propertyId,
2051                         (PVOID *)&fdoExtension->AdapterDescriptor);
2052             if (!NT_SUCCESS(status)) {
2053                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "ClassPnpStartDevice: ClassGetDescriptor [ADAPTER] failed %lx\n", status));
2054                 return status;
2055             }
2056 
2057             //
2058             // Call port driver to get device descriptor.
2059             //
2060 
2061             propertyId = StorageDeviceProperty;
2062 
2063             status = ClassGetDescriptor(
2064                         commonExtension->LowerDeviceObject,
2065                         &propertyId,
2066                         (PVOID *)&fdoExtension->DeviceDescriptor);
2067             if (NT_SUCCESS(status)){
2068 
2069                 ClasspScanForSpecialInRegistry(fdoExtension);
2070                 ClassScanForSpecial(fdoExtension, ClassBadItems, ClasspScanForClassHacks);
2071 
2072                 //
2073                 // allow perf to be re-enabled after a given number of failed IOs
2074                 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
2075                 //
2076 
2077                 {
2078                     ULONG t = CLASS_PERF_RESTORE_MINIMUM;
2079 
2080                     ClassGetDeviceParameter(fdoExtension,
2081                                             CLASSP_REG_SUBKEY_NAME,
2082                                             CLASSP_REG_PERF_RESTORE_VALUE_NAME,
2083                                             &t);
2084                     if (t >= CLASS_PERF_RESTORE_MINIMUM) {
2085                         fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
2086                     }
2087                 }
2088 
2089                 //
2090                 // compatibility comes first.  writable cd media will not
2091                 // get a SYNCH_CACHE on power down.
2092                 //
2093                 if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
2094                     SET_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_SYNC_CACHE);
2095                 }
2096 
2097 
2098                 //
2099                 // Test if the device is portable and updated the characteristics if so
2100                 //
2101                 status = ClasspIsPortable(fdoExtension,
2102                                           &isPortable);
2103 
2104                 if (NT_SUCCESS(status) && (isPortable == TRUE)) {
2105                     DeviceObject->Characteristics |= FILE_PORTABLE_DEVICE;
2106                 }
2107 
2108                 //
2109                 // initialize the hotplug information only after the ScanForSpecial
2110                 // routines, as it relies upon the hack flags.
2111                 //
2112                 status = ClasspInitializeHotplugInfo(fdoExtension);
2113                 if (NT_SUCCESS(status)){
2114                     /*
2115                      *  Allocate/initialize TRANSFER_PACKETs and related resources.
2116                      */
2117                     status = InitializeTransferPackets(DeviceObject);
2118                 }
2119                 else {
2120                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP,  "ClassPnpStartDevice: Could not initialize hotplug information %lx\n", status));
2121                 }
2122             }
2123             else {
2124                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "ClassPnpStartDevice: ClassGetDescriptor [DEVICE] failed %lx\n", status));
2125                 return status;
2126             }
2127 
2128 
2129             if (NT_SUCCESS(status)) {
2130 
2131                 //
2132                 // Retrieve info on whether async notification is supported by port drivers
2133                 //
2134                 propertyId = StorageDevicePowerProperty;
2135 
2136                 status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
2137                                             &propertyId,
2138                                             (PVOID *)&powerDescriptor);
2139                 if (NT_SUCCESS(status) && (powerDescriptor != NULL)) {
2140                     fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported = powerDescriptor->AsynchronousNotificationSupported;
2141                     fdoExtension->FunctionSupportInfo->IdlePower.D3ColdSupported = powerDescriptor->D3ColdSupported;
2142                     fdoExtension->FunctionSupportInfo->IdlePower.NoVerifyDuringIdlePower = powerDescriptor->NoVerifyDuringIdlePower;
2143                     FREE_POOL(powerDescriptor);
2144                 } else {
2145                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "ClassPnpStartDevice: ClassGetDescriptor [DevicePower] failed %lx\n", status));
2146 
2147                     //
2148                     // Ignore error as device power property is optional
2149                     //
2150                     status = STATUS_SUCCESS;
2151                 }
2152             }
2153         }
2154 
2155         //
2156         // ISSUE - drivers need to disable write caching on the media
2157         //         if hotplug and !useroverride.  perhaps we should
2158         //         allow registration of a callback to enable/disable
2159         //         write cache instead.
2160         //
2161 
2162         if (NT_SUCCESS(status)){
2163             status = devInfo->ClassInitDevice(DeviceObject);
2164         }
2165 
2166         if (commonExtension->IsFdo) {
2167             fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
2168 
2169             //
2170             // initialization for disk device
2171             //
2172             if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
2173                 (!TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
2174 
2175                 ULONG accessAlignmentNotSupported = 0;
2176                 ULONG qerrOverrideMode = QERR_SET_ZERO_ODX_OR_TP_ONLY;
2177                 ULONG legacyErrorHandling = FALSE;
2178 
2179                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,
2180                            "ClassPnpStartDevice: Enabling idle timer for %p\n", DeviceObject));
2181                 // Initialize idle timer for disk devices
2182                 ClasspInitializeIdleTimer(fdoExtension);
2183 
2184                 if (ClasspIsObsoletePortDriver(fdoExtension) == FALSE) {
2185                     // get INQUIRY VPD support information. It's safe to send command as everything is ready in ClassInitDevice().
2186                     ClasspGetInquiryVpdSupportInfo(fdoExtension);
2187 
2188                     // Query and cache away Logical Block Provisioning info in the FDO extension.
2189                     // The cached information will be used in responding to some IOCTLs
2190                     ClasspGetLBProvisioningInfo(fdoExtension);
2191 
2192                     //
2193                     // Query and cache away Block Device ROD Limits info in the FDO extension.
2194                     //
2195                     if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits) {
2196                         ClassDetermineTokenOperationCommandSupport(DeviceObject);
2197                     }
2198 
2199                     //
2200                     // See if the user has specified a particular QERR override
2201                     // mode. "Override" meaning setting QERR = 0 via Mode Select.
2202                     //  0 = Only when ODX or Thin Provisioning are supported (default)
2203                     //  1 = Always
2204                     //  2 = Never (or any value >= 2)
2205                     //
2206                     ClassGetDeviceParameter(fdoExtension,
2207                                             CLASSP_REG_SUBKEY_NAME,
2208                                             CLASSP_REG_QERR_OVERRIDE_MODE,
2209                                             &qerrOverrideMode);
2210 
2211                     //
2212                     // If this device is thinly provisioned or supports ODX, we
2213                     // may need to force QERR to zero.  The user may have also
2214                     // specified that we should always or never do this.
2215                     //
2216                     if (qerrOverrideMode == QERR_SET_ZERO_ALWAYS ||
2217                         (qerrOverrideMode == QERR_SET_ZERO_ODX_OR_TP_ONLY &&
2218                          (ClasspIsThinProvisioned(fdoExtension->FunctionSupportInfo) ||
2219                           NT_SUCCESS(ClasspValidateOffloadSupported(DeviceObject, NULL))))) {
2220 
2221                         ClasspZeroQERR(DeviceObject);
2222                     }
2223 
2224                 } else {
2225 
2226                     //
2227                     // Since this device has been exposed by a legacy miniport (e.g. SCSIPort miniport)
2228                     // set its LB Provisioning command status to an error status that will be surfaced
2229                     // up to the caller of a TRIM/Unmap command.
2230                     //
2231                     fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = STATUS_UNSUCCESSFUL;
2232                     fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus = STATUS_UNSUCCESSFUL;
2233                     fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = STATUS_UNSUCCESSFUL;
2234                 }
2235 
2236                 // Get registry setting of failing the IOCTL for AccessAlignment Property.
2237                 ClassGetDeviceParameter(fdoExtension,
2238                                         CLASSP_REG_SUBKEY_NAME,
2239                                         CLASSP_REG_ACCESS_ALIGNMENT_NOT_SUPPORTED,
2240                                         &accessAlignmentNotSupported);
2241 
2242                 if (accessAlignmentNotSupported > 0) {
2243                     fdoExtension->FunctionSupportInfo->RegAccessAlignmentQueryNotSupported = TRUE;
2244                 }
2245 
2246 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
2247 
2248 
2249                 //
2250                 // See if the user has specified legacy error handling.
2251                 //
2252                 ClassGetDeviceParameter(fdoExtension,
2253                                         CLASSP_REG_SUBKEY_NAME,
2254                                         CLASSP_REG_LEGACY_ERROR_HANDLING,
2255                                         &legacyErrorHandling);
2256 
2257                 if (legacyErrorHandling) {
2258                     //
2259                     // Legacy error handling means that the maximum number of
2260                     // retries allowd for an IO request is 8 instead of 4.
2261                     //
2262                     fdoExtension->PrivateFdoData->MaxNumberOfIoRetries = LEGACY_NUM_IO_RETRIES;
2263                     fdoExtension->PrivateFdoData->LegacyErrorHandling = TRUE;
2264                 }
2265 #else
2266                 UNREFERENCED_PARAMETER(legacyErrorHandling);
2267 #endif
2268 
2269 
2270                 //
2271                 // Get the copy offload max target duration value.
2272                 // This function will set the default value if one hasn't been
2273                 // specified in the registry.
2274                 //
2275                 ClasspGetCopyOffloadMaxDuration(DeviceObject,
2276                                                 REG_DISK_CLASS_CONTROL,
2277                                                 &(fdoExtension->PrivateFdoData->CopyOffloadMaxTargetDuration));
2278 
2279             }
2280 
2281         }
2282     }
2283 
2284     if (!NT_SUCCESS(status)){
2285 
2286         //
2287         // Just bail out - the remove that comes down will clean up the
2288         // initialized scraps.
2289         //
2290 
2291         return status;
2292     } else {
2293         commonExtension->IsInitialized = TRUE;
2294     }
2295 
2296     //
2297     // If device requests autorun functionality or a once a second callback
2298     // then enable the once per second timer. Exception is if media change
2299     // detection is desired but device supports async notification.
2300     //
2301     // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
2302     //       called in the context of the ClassInitDevice callback. If called
2303     //       after then this check will have already been made and the
2304     //       once a second timer will not have been enabled.
2305     //
2306     if ((isFdo) &&
2307         ((initData->ClassTick != NULL) ||
2308          ((fdoExtension->MediaChangeDetectionInfo != NULL) &&
2309           (fdoExtension->FunctionSupportInfo != NULL) &&
2310           (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) ||
2311          ((fdoExtension->FailurePredictionInfo != NULL) &&
2312           (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
2313     {
2314         ClasspEnableTimer(fdoExtension);
2315 
2316         //
2317         // In addition, we may change our polling behavior when the screen is
2318         // off so register for screen state notification if we haven't already
2319         // done so.
2320         //
2321         if (ScreenStateNotificationHandle == NULL) {
2322             PoRegisterPowerSettingCallback(DeviceObject,
2323                                             &GUID_CONSOLE_DISPLAY_STATE,
2324                                             &ClasspPowerSettingCallback,
2325                                             NULL,
2326                                             &ScreenStateNotificationHandle);
2327         }
2328     }
2329 
2330     //
2331     // NOTE: the timer looks at commonExtension->CurrentState now
2332     //       to prevent Media Change Notification code from running
2333     //       until the device is started, but allows the device
2334     //       specific tick handler to run.  therefore it is imperative
2335     //       that commonExtension->CurrentState not be updated until
2336     //       the device specific startdevice handler has finished.
2337     //
2338 
2339     status = devInfo->ClassStartDevice(DeviceObject);
2340 
2341     if (NT_SUCCESS(status)){
2342         commonExtension->CurrentState = IRP_MN_START_DEVICE;
2343 
2344         if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
2345             isMountedDevice = FALSE;
2346         }
2347 
2348         if (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) {
2349 
2350             isMountedDevice = FALSE;
2351         }
2352 
2353         //
2354         // Register for mounted device interface if this is a
2355         // sfloppy device.
2356         //
2357         if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
2358             (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
2359 
2360             isMountedDevice = TRUE;
2361         }
2362 
2363         if(isMountedDevice) {
2364             ClasspRegisterMountedDeviceInterface(DeviceObject);
2365         }
2366 
2367         if(commonExtension->IsFdo) {
2368             IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
2369 
2370             //
2371             // Tell Storport (Usbstor or SD) to enable idle power management for this
2372             // device, assuming the user hasn't turned it off in the registry.
2373             //
2374             if (fdoExtension->FunctionSupportInfo != NULL &&
2375                 fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled == FALSE &&
2376                 fdoExtension->MiniportDescriptor != NULL &&
2377                 (fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetStorport ||
2378                  fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSDport ||
2379                  fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetUSBport)) {
2380                 ULONG disableIdlePower= 0;
2381                 ClassGetDeviceParameter(fdoExtension,
2382                                         CLASSP_REG_SUBKEY_NAME,
2383                                         CLASSP_REG_DISBALE_IDLE_POWER_NAME,
2384                                         &disableIdlePower);
2385 
2386                 if (!disableIdlePower) {
2387                     ClasspEnableIdlePower(DeviceObject);
2388                 }
2389             }
2390         }
2391     }
2392     else {
2393         ClasspDisableTimer(fdoExtension);
2394     }
2395 
2396 
2397     return status;
2398 }
2399 
2400 
2401 /*++////////////////////////////////////////////////////////////////////////////
2402 
2403 ClassReadWrite()
2404 
2405 Routine Description:
2406 
2407     This is the system entry point for read and write requests. The
2408     device-specific handler is invoked to perform any validation necessary.
2409 
2410     If the device object is a PDO (partition object) then the request will
2411     simply be adjusted for Partition0 and issued to the lower device driver.
2412 
2413     IF the device object is an FDO (paritition 0 object), the number of bytes
2414     in the request are checked against the maximum byte counts that the adapter
2415     supports and requests are broken up into
2416     smaller sizes if necessary.
2417 
2418 Arguments:
2419 
2420     DeviceObject - a pointer to the device object for this request
2421 
2422     Irp - IO request
2423 
2424 Return Value:
2425 
2426     NT Status
2427 
2428 --*/
2429 NTSTATUS
2430 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassReadWrite(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)2431 ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
2432 {
2433     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
2434     PDEVICE_OBJECT      lowerDeviceObject = commonExtension->LowerDeviceObject;
2435     PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
2436     ULONG               transferByteCount = currentIrpStack->Parameters.Read.Length;
2437     ULONG               isRemoved;
2438     NTSTATUS            status;
2439 
2440     /*
2441      *  Grab the remove lock.  If we can't acquire it, bail out.
2442      */
2443     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
2444     if (isRemoved) {
2445         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
2446         Irp->IoStatus.Information = 0;
2447         ClassReleaseRemoveLock(DeviceObject, Irp);
2448         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2449         status = STATUS_DEVICE_DOES_NOT_EXIST;
2450     }
2451     else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
2452              (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
2453              !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
2454 
2455         /*
2456          *  DO_VERIFY_VOLUME is set for the device object,
2457          *  but this request is not itself a verify request.
2458          *  So fail this request.
2459          */
2460         if (Irp->Tail.Overlay.Thread != NULL) {
2461             IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
2462         }
2463         Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
2464         Irp->IoStatus.Information = 0;
2465         ClassReleaseRemoveLock(DeviceObject, Irp);
2466         ClassCompleteRequest(DeviceObject, Irp, 0);
2467         status = STATUS_VERIFY_REQUIRED;
2468     }
2469     else {
2470 
2471         /*
2472          *  Since we've bypassed the verify-required tests we don't need to repeat
2473          *  them with this IRP - in particular we don't want to worry about
2474          *  hitting them at the partition 0 level if the request has come through
2475          *  a non-zero partition.
2476          */
2477         currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
2478 
2479         /*
2480          *  Call the miniport driver's pre-pass filter to check if we
2481          *  should continue with this transfer.
2482          */
2483         NT_ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
2484         status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
2485         // Code Analysis cannot analyze the code paths specific to clients.
2486         _Analysis_assume_(status != STATUS_PENDING);
2487         if (!NT_SUCCESS(status)){
2488             NT_ASSERT(Irp->IoStatus.Status == status);
2489             Irp->IoStatus.Information = 0;
2490             ClassReleaseRemoveLock(DeviceObject, Irp);
2491             ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
2492         }
2493         else if (status == STATUS_PENDING){
2494             /*
2495              *  ClassReadWriteVerification queued this request.
2496              *  So don't touch the irp anymore.
2497              */
2498         }
2499         else {
2500 
2501             if (transferByteCount == 0) {
2502                 /*
2503                  *  Several parts of the code turn 0 into 0xffffffff,
2504                  *  so don't process a zero-length request any further.
2505                  */
2506                 Irp->IoStatus.Status = STATUS_SUCCESS;
2507                 Irp->IoStatus.Information = 0;
2508                 ClassReleaseRemoveLock(DeviceObject, Irp);
2509                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2510                 status = STATUS_SUCCESS;
2511             }
2512             else {
2513                 /*
2514                  *  If the driver has its own StartIo routine, call it.
2515                  */
2516                 if (commonExtension->DriverExtension->InitData.ClassStartIo) {
2517                     IoMarkIrpPending(Irp);
2518                     IoStartPacket(DeviceObject, Irp, NULL, NULL);
2519                     status = STATUS_PENDING;
2520                 }
2521                 else {
2522                     /*
2523                      *  The driver does not have its own StartIo routine.
2524                      *  So process this request ourselves.
2525                      */
2526 
2527                     /*
2528                      *  Add partition byte offset to make starting byte relative to
2529                      *  beginning of disk.
2530                      */
2531                     currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
2532                         commonExtension->StartingOffset.QuadPart;
2533 
2534                     if (commonExtension->IsFdo){
2535 
2536                         PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
2537                         PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
2538 
2539                         /*
2540                          *  Add in any skew for the disk manager software.
2541                          */
2542                         currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
2543                              commonExtension->PartitionZeroExtension->DMByteSkew;
2544 
2545                         //
2546                         // In case DEV_USE_16BYTE_CDB flag is not set, R/W request will be translated into READ/WRITE 10 SCSI command.
2547                         // These SCSI commands have 4 bytes in "Starting LBA" field.
2548                         // Requests cannot be represented in these SCSI commands should be failed.
2549                         //
2550                         if (!TEST_FLAG(fdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
2551                             LARGE_INTEGER startingLba;
2552 
2553                             startingLba.QuadPart = currentIrpStack->Parameters.Read.ByteOffset.QuadPart >> fdoExtension->SectorShift;
2554 
2555                             if (startingLba.QuadPart > MAXULONG) {
2556                                 Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
2557                                 Irp->IoStatus.Information = 0;
2558                                 ClassReleaseRemoveLock(DeviceObject, Irp);
2559                                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
2560                                 status = STATUS_INVALID_PARAMETER;
2561                                 goto Exit;
2562                             }
2563                         }
2564 
2565 #if DBG
2566                         //
2567                         // Record the caller if:
2568                         // 1. the disk is currently off
2569                         // 2. the operation is a read, (likely resulting in disk spinnage)
2570                         // 3. the operation is marked WT (likely resulting in disk spinnage)
2571                         //
2572                         if((fdoExtension->DevicePowerState == PowerDeviceD3) && // disk is off
2573                             ((currentIrpStack->MajorFunction == IRP_MJ_READ) ||                       // It's a read.
2574                              (TEST_FLAG(currentIrpStack->Flags, SL_WRITE_THROUGH))) ) {               // they *really* want it to go to disk.
2575 
2576                             SnapDiskStartup();
2577                         }
2578 #endif
2579 
2580                         /*
2581                          *  Perform the actual transfer(s) on the hardware
2582                          *  to service this request.
2583                          */
2584                         if (ClasspIsIdleRequestSupported(fdoData, Irp)) {
2585                             ClasspMarkIrpAsIdle(Irp, TRUE);
2586                             status = ClasspEnqueueIdleRequest(DeviceObject, Irp);
2587                         } else {
2588                             UCHAR uniqueAddr = 0;
2589 
2590                             //
2591                             // Since we're touching fdoData after servicing the transfer packet, this opens us up to
2592                             // a potential window, where the device may be removed between the time that
2593                             // ServiceTransferPacket completes and we've had a chance to access fdoData. In order
2594                             // to guard against this, we acquire the removelock an additional time here. This
2595                             // acquire is guaranteed to succeed otherwise we wouldn't be here (because of the
2596                             // outer acquire).
2597                             // The sequence of events we're guarding against with this remLock acquire is:
2598                             // 1. This UL IRP acquired the lock.
2599                             // 2. Device gets surprised removed, then gets IRP_MN_REMOVE_DEVICE; ClassRemoveDevice
2600                             //    waits for the above RemoveLock.
2601                             // 3. ServiceTransferRequest breaks the UL IRP into DL IRPs.
2602                             // 4. DL IRPs complete with STATUS_NO_SUCH_DEVICE and TransferPktComplete completes the UL
2603                             //    IRP with STATUS_NO_SUCH_DEVICE; releases the RemoveLock.
2604                             // 5. ClassRemoveDevice is now unblocked, continues running and frees resources (including
2605                             //    fdoData).
2606                             // 6. Finally ClassReadWrite gets to run again and accesses a freed fdoData when trying to
2607                             //    check/update idle-related fields.
2608                             //
2609                             ClassAcquireRemoveLock(DeviceObject, (PVOID)&uniqueAddr);
2610 
2611                             ClasspMarkIrpAsIdle(Irp, FALSE);
2612                             status = ServiceTransferRequest(DeviceObject, Irp, FALSE);
2613                             if (fdoData->IdlePrioritySupported == TRUE) {
2614                                 fdoData->LastNonIdleIoTime = ClasspGetCurrentTime();
2615                             }
2616 
2617                             ClassReleaseRemoveLock(DeviceObject, (PVOID)&uniqueAddr);
2618                         }
2619                     }
2620                     else {
2621                         /*
2622                          *  This is a child PDO enumerated for our FDO by e.g. disk.sys
2623                          *  and owned by e.g. partmgr.  Send it down to the next device
2624                          *  and the same irp will come back to us for the FDO.
2625                          */
2626                         IoCopyCurrentIrpStackLocationToNext(Irp);
2627                         ClassReleaseRemoveLock(DeviceObject, Irp);
2628                         status = IoCallDriver(lowerDeviceObject, Irp);
2629                     }
2630                 }
2631             }
2632         }
2633     }
2634 
2635 Exit:
2636     return status;
2637 }
2638 
2639 
InterpretCapacityData(PDEVICE_OBJECT Fdo,PREAD_CAPACITY_DATA_EX ReadCapacityData)2640 VOID InterpretCapacityData(PDEVICE_OBJECT Fdo, PREAD_CAPACITY_DATA_EX ReadCapacityData)
2641 {
2642     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
2643     ULONG cylinderSize;
2644     ULONG bytesPerSector;
2645     LARGE_INTEGER lastSector;
2646     LARGE_INTEGER largeInt;
2647 
2648     bytesPerSector = ClasspCalculateLogicalSectorSize(Fdo, ReadCapacityData->BytesPerBlock);
2649 
2650     fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
2651     WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
2652 
2653     /*
2654      *  LogicalBlockAddress is the last sector of the logical drive, in big-endian.
2655      *  It tells us the size of the drive (#sectors is lastSector+1).
2656      */
2657 
2658     largeInt = ReadCapacityData->LogicalBlockAddress;
2659     REVERSE_BYTES_QUAD(&lastSector, &largeInt);
2660 
2661     if (fdoExt->DMActive){
2662         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,  "ClassReadDriveCapacity: reducing number of sectors by %d\n", fdoExt->DMSkew));
2663         lastSector.QuadPart -= fdoExt->DMSkew;
2664     }
2665 
2666     /*
2667      *  Check to see if we have a geometry we should be using already.
2668      *  If not, we set part of the disk geometry to garbage values that will be filled in by the caller (e.g. disk.sys).
2669      *
2670      *  So the first call to ClassReadDriveCapacity always sets a meaningless geometry.
2671      *  TracksPerCylinder and SectorsPerTrack are kind of meaningless anyway wrt I/O,
2672      *  because I/O is always targeted to a logical sector number.
2673      *  All that really matters is BytesPerSector and the number of sectors.
2674      */
2675     cylinderSize = fdoExt->DiskGeometry.TracksPerCylinder * fdoExt->DiskGeometry.SectorsPerTrack;
2676     if (cylinderSize == 0){
2677         fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
2678         fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
2679         cylinderSize = fdoExt->DiskGeometry.TracksPerCylinder * fdoExt->DiskGeometry.SectorsPerTrack;
2680     }
2681 
2682     /*
2683      *  Calculate number of cylinders.
2684      *  If there are zero cylinders, then the device lied AND it's
2685      *  smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
2686      *  this can fit into a single LONGLONG, so create another usable
2687      *  geometry, even if it's unusual looking.
2688      *  This allows small, non-standard devices, such as Sony's Memory Stick, to show up as having a partition.
2689      */
2690     fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector.QuadPart + 1)/cylinderSize);
2691     if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
2692         fdoExt->DiskGeometry.SectorsPerTrack    = 1;
2693         fdoExt->DiskGeometry.TracksPerCylinder  = 1;
2694         fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector.QuadPart + 1;
2695     }
2696 
2697     /*
2698      *  Calculate media capacity in bytes.
2699      *  For this purpose we treat the entire LUN as is if it is one partition.  Disk will deal with actual partitioning.
2700      */
2701     fdoExt->CommonExtension.PartitionLength.QuadPart =
2702         ((LONGLONG)(lastSector.QuadPart + 1)) << fdoExt->SectorShift;
2703 
2704     /*
2705      *  Is this removable or fixed media
2706      */
2707     if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
2708         fdoExt->DiskGeometry.MediaType = RemovableMedia;
2709     }
2710     else {
2711         fdoExt->DiskGeometry.MediaType = FixedMedia;
2712     }
2713 
2714 }
2715 
2716 /*++////////////////////////////////////////////////////////////////////////////
2717 
2718 ClassReadDriveCapacity()
2719 
2720 Routine Description:
2721 
2722     This routine sends a READ CAPACITY to the requested device, updates
2723     the geometry information in the device object and returns
2724     when it is complete.  This routine is synchronous.
2725 
2726     This routine must be called with the remove lock held or some other
2727     assurance that the Fdo will not be removed while processing.
2728 
2729 Arguments:
2730 
2731     DeviceObject - Supplies a pointer to the device object that represents
2732         the device whose capacity is to be read.
2733 
2734 Return Value:
2735 
2736     Status is returned.
2737 
2738 --*/
2739 _Must_inspect_result_
2740 NTSTATUS
2741 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassReadDriveCapacity(_In_ PDEVICE_OBJECT Fdo)2742 ClassReadDriveCapacity(_In_ PDEVICE_OBJECT Fdo)
2743 {
2744     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
2745     PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
2746     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
2747     READ_CAPACITY_DATA_EX PTRALIGN readCapacityData = {0};
2748     PTRANSFER_PACKET pkt;
2749     NTSTATUS status;
2750     PMDL driveCapMdl = NULL;
2751     KEVENT event;
2752     IRP pseudoIrp = {0};
2753     ULONG readCapacityDataSize;
2754     BOOLEAN use16ByteCdb;
2755     BOOLEAN match = TRUE;
2756 
2757     use16ByteCdb = TEST_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB);
2758 
2759 RetryRequest:
2760 
2761     if (use16ByteCdb) {
2762         readCapacityDataSize = sizeof(READ_CAPACITY_DATA_EX);
2763     } else {
2764         readCapacityDataSize = sizeof(READ_CAPACITY_DATA);
2765     }
2766 
2767     if (driveCapMdl != NULL) {
2768         FreeDeviceInputMdl(driveCapMdl);
2769         driveCapMdl = NULL;
2770     }
2771 
2772     //
2773     // Allocate the MDL based on the Read Capacity command.
2774     //
2775     driveCapMdl = BuildDeviceInputMdl(&readCapacityData, readCapacityDataSize);
2776     if (driveCapMdl == NULL) {
2777         status = STATUS_INSUFFICIENT_RESOURCES;
2778         goto SafeExit;
2779     }
2780 
2781     pkt = DequeueFreeTransferPacket(Fdo, TRUE);
2782     if (pkt == NULL) {
2783         status = STATUS_INSUFFICIENT_RESOURCES;
2784         goto SafeExit;
2785     }
2786 
2787     //
2788     //  Our engine needs an "original irp" to write the status back to
2789     //  and to count down packets (one in this case).
2790     //  Just use a pretend irp for this.
2791     //
2792 
2793     pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
2794     pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
2795     pseudoIrp.IoStatus.Information = 0;
2796     pseudoIrp.MdlAddress = driveCapMdl;
2797 
2798     //
2799     //  Set this up as a SYNCHRONOUS transfer, submit it,
2800     //  and wait for the packet to complete.  The result
2801     //  status will be written to the original irp.
2802     //
2803 
2804     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
2805     SetupDriveCapacityTransferPacket(pkt,
2806                                      &readCapacityData,
2807                                      readCapacityDataSize,
2808                                      &event,
2809                                      &pseudoIrp,
2810                                      use16ByteCdb);
2811     SubmitTransferPacket(pkt);
2812     (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
2813 
2814     status = pseudoIrp.IoStatus.Status;
2815 
2816     //
2817     //  If we got an UNDERRUN, retry exactly once.
2818     //  (The transfer_packet engine didn't retry because the result
2819     //   status was success).
2820     //
2821 
2822     if (NT_SUCCESS(status) &&
2823        (pseudoIrp.IoStatus.Information < readCapacityDataSize)) {
2824 
2825         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...",
2826                 (ULONG)pseudoIrp.IoStatus.Information, readCapacityDataSize));
2827 
2828         pkt = DequeueFreeTransferPacket(Fdo, TRUE);
2829         if (pkt) {
2830             pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
2831             pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
2832             pseudoIrp.IoStatus.Information = 0;
2833             KeInitializeEvent(&event, SynchronizationEvent, FALSE);
2834             SetupDriveCapacityTransferPacket(pkt,
2835                                              &readCapacityData,
2836                                              readCapacityDataSize,
2837                                              &event,
2838                                              &pseudoIrp,
2839                                              use16ByteCdb);
2840             SubmitTransferPacket(pkt);
2841             (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
2842             status = pseudoIrp.IoStatus.Status;
2843             if (pseudoIrp.IoStatus.Information < readCapacityDataSize){
2844                 status = STATUS_DEVICE_BUSY;
2845             }
2846         } else {
2847             status = STATUS_INSUFFICIENT_RESOURCES;
2848         }
2849     }
2850 
2851     if (NT_SUCCESS(status)) {
2852         //
2853         //  The request succeeded. Check for 8 byte LBA support.
2854         //
2855 
2856         if (use16ByteCdb == FALSE) {
2857 
2858             PREAD_CAPACITY_DATA readCapacity;
2859 
2860             //
2861             // Check whether the device supports 8 byte LBA. If the device supports
2862             // it then retry the request using 16 byte CDB.
2863             //
2864 
2865             readCapacity = (PREAD_CAPACITY_DATA) &readCapacityData;
2866 
2867             if (readCapacity->LogicalBlockAddress == 0xFFFFFFFF) {
2868                 //
2869                 // Device returned max size for last LBA. Need to send
2870                 // 16 byte request to get the size.
2871                 //
2872                 use16ByteCdb = TRUE;
2873                 goto RetryRequest;
2874 
2875             } else {
2876                 //
2877                 // Convert the 4 byte LBA (READ_CAPACITY_DATA) to 8 byte LBA (READ_CAPACITY_DATA_EX)
2878                 // format for ease of use. This is the only format stored in the device extension.
2879                 //
2880 
2881                 RtlMoveMemory((PUCHAR)(&readCapacityData) + sizeof(ULONG), readCapacity, sizeof(READ_CAPACITY_DATA));
2882                 RtlZeroMemory((PUCHAR)(&readCapacityData), sizeof(ULONG));
2883 
2884             }
2885         } else {
2886             //
2887             // Device completed 16 byte command successfully, it supports 8-byte LBA.
2888             //
2889 
2890             SET_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB);
2891         }
2892 
2893         //
2894         // Read out and store the drive information.
2895         //
2896 
2897         InterpretCapacityData(Fdo, &readCapacityData);
2898 
2899         //
2900         // Before caching the new drive capacity, compare it with the
2901         // cached capacity for any change.
2902         //
2903 
2904         if (fdoData->IsCachedDriveCapDataValid == TRUE) {
2905 
2906             match = (BOOLEAN) RtlEqualMemory(&fdoData->LastKnownDriveCapacityData,
2907                                     &readCapacityData, sizeof(READ_CAPACITY_DATA_EX));
2908         }
2909 
2910         //
2911         // Store the readCapacityData in private FDO data.
2912         // This is so that runtime memory failures don't cause disk.sys to put
2913         // the paging disk in an error state. Also this is used in
2914         // IOCTL_STORAGE_READ_CAPACITY.
2915         //
2916         fdoData->LastKnownDriveCapacityData = readCapacityData;
2917         fdoData->IsCachedDriveCapDataValid = TRUE;
2918 
2919         if (match == FALSE) {
2920             if (commonExtension->CurrentState != IRP_MN_START_DEVICE)
2921             {
2922                 //
2923                 // This can happen if a disk reports Parameters Changed / Capacity Data Changed sense data.
2924                 // NT_ASSERT(!"Drive capacity has changed while the device wasn't started!");
2925                 //
2926             } else {
2927                 //
2928                 // state of (commonExtension->CurrentState == IRP_MN_START_DEVICE) indicates that the device has been started.
2929                 // UpdateDiskPropertiesWorkItemActive is used as a flag to ensure we only have one work item updating the disk
2930                 // properties at a time.
2931                 //
2932                 if (InterlockedCompareExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 1, 0) == 0)
2933                 {
2934                     PIO_WORKITEM workItem;
2935 
2936                     workItem = IoAllocateWorkItem(Fdo);
2937 
2938                     if (workItem) {
2939                         //
2940                         // The disk capacity has changed, send notification to the disk driver.
2941                         // Start a work item to notify the disk class driver asynchronously.
2942                         //
2943                         IoQueueWorkItem(workItem, ClasspUpdateDiskProperties, DelayedWorkQueue, workItem);
2944                     } else {
2945                         InterlockedExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 0);
2946                     }
2947                 }
2948             }
2949         }
2950 
2951     } else {
2952         //
2953         //  The request failed.
2954         //
2955 
2956         //
2957         // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
2958         //    what happens when the disk's sector size is bigger than
2959         //    512 bytes and we hit this code path?  this is untested.
2960         //
2961         // If the read capacity fails, set the geometry to reasonable parameter
2962         // so things don't fail at unexpected places.  Zero the geometry
2963         // except for the bytes per sector and sector shift.
2964         //
2965 
2966         //
2967         //  This request can sometimes fail legitimately
2968         //  (e.g. when a SCSI device is attached but turned off)
2969         //  so this is not necessarily a device/driver bug.
2970         //
2971 
2972         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassReadDriveCapacity on Fdo %p failed with status %ul.", Fdo, status));
2973 
2974         //
2975         //  Write in a default disk geometry which we HOPE is right (??).
2976         //
2977 
2978         RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
2979         fdoExt->DiskGeometry.BytesPerSector = 512;
2980         fdoExt->SectorShift = 9;
2981         fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
2982 
2983         //
2984         //  Is this removable or fixed media
2985         //
2986 
2987         if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
2988             fdoExt->DiskGeometry.MediaType = RemovableMedia;
2989         } else {
2990             fdoExt->DiskGeometry.MediaType = FixedMedia;
2991         }
2992     }
2993 
2994 SafeExit:
2995 
2996 
2997     //
2998     // In case DEV_USE_16BYTE_CDB flag is not set, classpnp translates R/W request into READ/WRITE 10 SCSI command.
2999     // These SCSI commands have 2 bytes in "Transfer Blocks" field.
3000     // Make sure this max length (0xFFFF * sector size) is respected during request split.
3001     //
3002     if (!TEST_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB)) {
3003         ULONG cdb10MaxBlocks = ((ULONG)USHORT_MAX) << fdoExt->SectorShift;
3004 
3005         fdoData->HwMaxXferLen = min(cdb10MaxBlocks, fdoData->HwMaxXferLen);
3006     }
3007 
3008     if (driveCapMdl != NULL) {
3009         FreeDeviceInputMdl(driveCapMdl);
3010     }
3011 
3012     //
3013     // If the request failed for some reason then invalidate the cached
3014     // capacity data for removable devices. So that we won't return
3015     // wrong capacity in IOCTL_STORAGE_READ_CAPACITY
3016     //
3017 
3018     if (!NT_SUCCESS(status) && (fdoExt->DiskGeometry.MediaType == RemovableMedia)) {
3019         fdoData->IsCachedDriveCapDataValid = FALSE;
3020     }
3021 
3022     //
3023     //  Don't let memory failures (either here or in the port driver) in the ReadDriveCapacity call
3024     //  put the paging disk in an error state such that paging fails.
3025     //  Return the last known drive capacity (which may possibly be slightly out of date, even on
3026     //  fixed media, e.g. for storage cabinets that can grow a logical disk).
3027     //
3028     if ((status == STATUS_INSUFFICIENT_RESOURCES) &&
3029         (fdoData->IsCachedDriveCapDataValid) &&
3030         (fdoExt->DiskGeometry.MediaType == FixedMedia)) {
3031         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassReadDriveCapacity: defaulting to cached DriveCapacity data"));
3032         InterpretCapacityData(Fdo, &fdoData->LastKnownDriveCapacityData);
3033         status = STATUS_SUCCESS;
3034     }
3035 
3036     return status;
3037 }
3038 
3039 
3040 /*++////////////////////////////////////////////////////////////////////////////
3041 
3042 ClassSendStartUnit()
3043 
3044 Routine Description:
3045 
3046     Send command to SCSI unit to start or power up.
3047     Because this command is issued asynchronounsly, that is, without
3048     waiting on it to complete, the IMMEDIATE flag is not set. This
3049     means that the CDB will not return until the drive has powered up.
3050     This should keep subsequent requests from being submitted to the
3051     device before it has completely spun up.
3052 
3053     This routine is called from the InterpretSense routine, when a
3054     request sense returns data indicating that a drive must be
3055     powered up.
3056 
3057     This routine may also be called from a class driver's error handler,
3058     or anytime a non-critical start device should be sent to the device.
3059 
3060 Arguments:
3061 
3062     Fdo - The functional device object for the stopped device.
3063 
3064 Return Value:
3065 
3066     None.
3067 
3068 --*/
3069 VOID
3070 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassSendStartUnit(_In_ PDEVICE_OBJECT Fdo)3071 ClassSendStartUnit(
3072     _In_ PDEVICE_OBJECT Fdo
3073     )
3074 {
3075     PIO_STACK_LOCATION irpStack;
3076     PIRP irp;
3077     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
3078     PSCSI_REQUEST_BLOCK srb;
3079     PCOMPLETION_CONTEXT context;
3080     PCDB cdb;
3081     NTSTATUS status;
3082     PSTORAGE_REQUEST_BLOCK srbEx;
3083 
3084     //
3085     // Allocate Srb from nonpaged pool.
3086     //
3087 
3088     context = ExAllocatePoolWithTag(NonPagedPoolNx,
3089                              sizeof(COMPLETION_CONTEXT),
3090                              '6CcS');
3091 
3092     if (context == NULL) {
3093 
3094         //
3095         // ISSUE-2000/02/03-peterwie
3096         // This code path was inheritted from the NT 4.0 class2.sys driver.
3097         // It needs to be changed to survive low-memory conditions.
3098         //
3099 
3100         KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
3101     }
3102 
3103     //
3104     // Save the device object in the context for use by the completion
3105     // routine.
3106     //
3107 
3108     context->DeviceObject = Fdo;
3109 
3110     srb = &context->Srb.Srb;
3111     if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
3112         srbEx = &context->Srb.SrbEx;
3113         status = InitializeStorageRequestBlock(srbEx,
3114                                                STORAGE_ADDRESS_TYPE_BTL8,
3115                                                sizeof(context->Srb.SrbExBuffer),
3116                                                1,
3117                                                SrbExDataTypeScsiCdb16);
3118         if (!NT_SUCCESS(status)) {
3119             FREE_POOL(context);
3120             NT_ASSERT(FALSE);
3121             return;
3122         }
3123 
3124         srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
3125 
3126     } else {
3127 
3128         //
3129         // Zero out srb.
3130         //
3131 
3132         RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
3133 
3134         //
3135         // Write length to SRB.
3136         //
3137 
3138         srb->Length = sizeof(SCSI_REQUEST_BLOCK);
3139 
3140         srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
3141     }
3142 
3143     //
3144     // Set timeout value large enough for drive to spin up.
3145     //
3146 
3147     SrbSetTimeOutValue(srb, START_UNIT_TIMEOUT);
3148 
3149     //
3150     // Set the transfer length.
3151     //
3152 
3153     SrbAssignSrbFlags(srb,
3154                       (SRB_FLAGS_NO_DATA_TRANSFER  |
3155                        SRB_FLAGS_DISABLE_AUTOSENSE |
3156                        SRB_FLAGS_DISABLE_SYNCH_TRANSFER));
3157 
3158     //
3159     // Build the start unit CDB.
3160     //
3161 
3162     SrbSetCdbLength(srb, 6);
3163     cdb = SrbGetCdb(srb);
3164 
3165     cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
3166     cdb->START_STOP.Start = 1;
3167     cdb->START_STOP.Immediate = 0;
3168     cdb->START_STOP.LogicalUnitNumber = srb->Lun;
3169 
3170     //
3171     // Build the asynchronous request to be sent to the port driver.
3172     // Since this routine is called from a DPC the IRP should always be
3173     // available.
3174     //
3175 
3176     irp = IoAllocateIrp(Fdo->StackSize, FALSE);
3177 
3178     if (irp == NULL) {
3179 
3180         //
3181         // ISSUE-2000/02/03-peterwie
3182         // This code path was inheritted from the NT 4.0 class2.sys driver.
3183         // It needs to be changed to survive low-memory conditions.
3184         //
3185 
3186         KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
3187 
3188     }
3189 
3190     ClassAcquireRemoveLock(Fdo, irp);
3191 
3192     IoSetCompletionRoutine(irp,
3193                            (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
3194                            context,
3195                            TRUE,
3196                            TRUE,
3197                            TRUE);
3198 
3199     irpStack = IoGetNextIrpStackLocation(irp);
3200     irpStack->MajorFunction = IRP_MJ_SCSI;
3201     SrbSetOriginalRequest(srb, irp);
3202 
3203     //
3204     // Store the SRB address in next stack for port driver.
3205     //
3206 
3207     irpStack->Parameters.Scsi.Srb = srb;
3208 
3209     //
3210     // Call the port driver with the IRP.
3211     //
3212 
3213     IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
3214 
3215     return;
3216 
3217 } // end StartUnit()
3218 
3219 /*++////////////////////////////////////////////////////////////////////////////
3220 
3221 ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
3222 
3223 Routine Description:
3224 
3225     This routine is called when an asynchronous I/O request
3226     which was issused by the class driver completes.  Examples of such requests
3227     are release queue or START UNIT. This routine releases the queue if
3228     necessary.  It then frees the context and the IRP.
3229 
3230 Arguments:
3231 
3232     DeviceObject - The device object for the logical unit; however since this
3233         is the top stack location the value is NULL.
3234 
3235     Irp - Supplies a pointer to the Irp to be processed.
3236 
3237     Context - Supplies the context to be used to process this request.
3238 
3239 Return Value:
3240 
3241     None.
3242 
3243 --*/
3244 NTSTATUS
3245 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassAsynchronousCompletion(PDEVICE_OBJECT DeviceObject,PIRP Irp,PVOID Context)3246 ClassAsynchronousCompletion(
3247     PDEVICE_OBJECT DeviceObject,
3248     PIRP Irp,
3249     PVOID Context
3250     )
3251 {
3252     PCOMPLETION_CONTEXT context = Context;
3253     PSCSI_REQUEST_BLOCK srb;
3254     ULONG srbFunction;
3255     ULONG srbFlags;
3256 
3257     if (context == NULL) {
3258         return STATUS_INVALID_PARAMETER;
3259     }
3260 
3261     if (DeviceObject == NULL) {
3262 
3263         DeviceObject = context->DeviceObject;
3264     }
3265 
3266     srb = &context->Srb.Srb;
3267 
3268     if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
3269         srbFunction = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction;
3270         srbFlags = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFlags;
3271     } else {
3272         srbFunction = srb->Function;
3273         srbFlags = srb->SrbFlags;
3274     }
3275 
3276     //
3277     // If this is an execute srb, then check the return status and make sure.
3278     // the queue is not frozen.
3279     //
3280 
3281     if (srbFunction == SRB_FUNCTION_EXECUTE_SCSI) {
3282 
3283         //
3284         // Check for a frozen queue.
3285         //
3286 
3287         if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
3288 
3289             //
3290             // Unfreeze the queue getting the device object from the context.
3291             //
3292 
3293             ClassReleaseQueue(context->DeviceObject);
3294         }
3295     }
3296 
3297     { // free port-allocated sense buffer if we can detect
3298 
3299         if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
3300 
3301             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
3302             if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
3303                 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
3304             }
3305 
3306         } else {
3307 
3308             NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
3309 
3310         }
3311     }
3312 
3313 
3314     //
3315     // Free the context and the Irp.
3316     //
3317 
3318     if (Irp->MdlAddress != NULL) {
3319         MmUnlockPages(Irp->MdlAddress);
3320         IoFreeMdl(Irp->MdlAddress);
3321 
3322         Irp->MdlAddress = NULL;
3323     }
3324 
3325     ClassReleaseRemoveLock(DeviceObject, Irp);
3326 
3327     FREE_POOL(context);
3328 
3329     IoFreeIrp(Irp);
3330 
3331     //
3332     // Indicate the I/O system should stop processing the Irp completion.
3333     //
3334 
3335     return STATUS_MORE_PROCESSING_REQUIRED;
3336 
3337 } // end ClassAsynchronousCompletion()
3338 
3339 
3340 NTSTATUS
ServiceTransferRequest(PDEVICE_OBJECT Fdo,PIRP Irp,BOOLEAN PostToDpc)3341 ServiceTransferRequest(
3342     PDEVICE_OBJECT Fdo,
3343     PIRP Irp,
3344     BOOLEAN PostToDpc
3345     )
3346 
3347 /*++
3348 
3349 Routine description:
3350 
3351     This routine processes Io requests, splitting them if they
3352     are larger than what the hardware can handle at a time. If
3353     there isn't enough memory available, the request is placed
3354     in a queue, to be processed at a later time
3355 
3356     If this is a high priority  paging request, all regular Io
3357     are throttled to provide Mm with better thoroughput
3358 
3359 Arguments:
3360 
3361     Fdo - The functional device object processing the request
3362     Irp - The Io request to be processed
3363     PostToDpc - Flag that indicates that this IRP must be posted to a DPC
3364 
3365 Return Value:
3366 
3367     STATUS_SUCCESS if successful, an error code otherwise
3368 
3369 --*/
3370 
3371 {
3372     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
3373     PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
3374     PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
3375     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
3376     IO_PAGING_PRIORITY priority = (TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) ? IoGetPagingIoPriority(Irp) : IoPagingPriorityInvalid;
3377     BOOLEAN deferClientIrp = FALSE;
3378     BOOLEAN driverUsesStartIO = (commonExtension->DriverExtension->InitData.ClassStartIo != NULL);
3379     KIRQL oldIrql;
3380     NTSTATUS status = STATUS_UNSUCCESSFUL;
3381     PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
3382 
3383     /*
3384      * Initialize IRP status for the master IRP to
3385      * - STATUS_FT_READ_FROM_COPY if it's a copy-specific read
3386      * - STATUS_SUCCESS otherwise.
3387      *
3388      * This is required. When Classpnp determines the status for the master IRP
3389      * when completing child IRPs, the call to IoSetMasterIrpStatus
3390      * will be functioning properly (See TransferPktComplete function)
3391      *
3392      * Note:
3393      * If the IRP is a copy-specific read, File System already initialized the IRP status
3394      * to be STATUS_FT_READ_FROM_COPY. However, this can be changed when the IRP arrives
3395      * at Classpnp. It's possible that other drivers in the stack may initialize the
3396      * IRP status field to other values before forwarding the IRP down the stack.
3397      * To be defensive, we initialize the IRP status to either STATUS_FT_READ_FROM_COPY
3398      * if it's a copy-specific read, or STATUS_SUCCESS otherwise.
3399      */
3400     if (currentIrpStack->MajorFunction == IRP_MJ_READ &&
3401         TEST_FLAG(currentIrpStack->Flags, SL_KEY_SPECIFIED) &&
3402         IsKeyReadCopyNumber(currentIrpStack->Parameters.Read.Key)) {
3403         Irp->IoStatus.Status = STATUS_FT_READ_FROM_COPY;
3404     } else {
3405         Irp->IoStatus.Status = STATUS_SUCCESS;
3406     }
3407 
3408     //
3409     // If this is a high priority request, hold off all other Io requests
3410     //
3411 
3412     if (priority == IoPagingPriorityHigh)
3413     {
3414         KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
3415 
3416         if (fdoData->NumHighPriorityPagingIo == 0)
3417         {
3418             //
3419             // Entering throttle mode
3420             //
3421 
3422             KeQuerySystemTime(&fdoData->ThrottleStartTime);
3423         }
3424 
3425         fdoData->NumHighPriorityPagingIo++;
3426         fdoData->MaxInterleavedNormalIo += ClassMaxInterleavePerCriticalIo;
3427 
3428         KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
3429     }
3430     else
3431     {
3432         if (fdoData->NumHighPriorityPagingIo != 0)
3433         {
3434             //
3435             // This request wasn't flagged as critical and atleast one critical request
3436             // is currently outstanding. Queue this request until all of those are done
3437             // but only if the interleave threshold has been reached
3438             //
3439 
3440             KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
3441 
3442             if (fdoData->NumHighPriorityPagingIo != 0)
3443             {
3444                 if (fdoData->MaxInterleavedNormalIo == 0)
3445                 {
3446                     deferClientIrp = TRUE;
3447                 }
3448                 else
3449                 {
3450                     fdoData->MaxInterleavedNormalIo--;
3451                 }
3452             }
3453 
3454             KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
3455         }
3456     }
3457 
3458     if (!deferClientIrp)
3459     {
3460         PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
3461         ULONG entireXferLen = currentSp->Parameters.Read.Length;
3462         PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
3463         LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
3464         PTRANSFER_PACKET pkt;
3465         SINGLE_LIST_ENTRY pktList;
3466         PSINGLE_LIST_ENTRY slistEntry;
3467         ULONG hwMaxXferLen;
3468         ULONG numPackets;
3469         ULONG i;
3470 
3471 
3472         /*
3473          *  We precomputed fdoData->HwMaxXferLen using (MaximumPhysicalPages-1).
3474          *  If the buffer is page-aligned, that's one less page crossing so we can add the page back in.
3475          *  Note: adapters that return MaximumPhysicalPages=0x10 depend on this to
3476          *           transfer aligned 64K requests in one piece.
3477          *  Also note:  make sure adding PAGE_SIZE back in doesn't wrap to zero.
3478          */
3479         if (((ULONG_PTR)bufPtr & (PAGE_SIZE-1)) || (fdoData->HwMaxXferLen > 0xffffffff-PAGE_SIZE)){
3480             hwMaxXferLen = fdoData->HwMaxXferLen;
3481         }
3482         else {
3483             NT_ASSERT((PAGE_SIZE%fdoExt->DiskGeometry.BytesPerSector) == 0);
3484             hwMaxXferLen = min(fdoData->HwMaxXferLen+PAGE_SIZE, adapterDesc->MaximumTransferLength);
3485         }
3486 
3487         /*
3488          *  Compute the number of hw xfers we'll have to do.
3489          *  Calculate this without allowing for an overflow condition.
3490          */
3491         NT_ASSERT(hwMaxXferLen >= PAGE_SIZE);
3492         numPackets = entireXferLen/hwMaxXferLen;
3493         if (entireXferLen % hwMaxXferLen){
3494             numPackets++;
3495         }
3496 
3497         /*
3498          *  Use our 'simple' slist functions since we don't need interlocked.
3499          */
3500         SimpleInitSlistHdr(&pktList);
3501 
3502         if (driverUsesStartIO) {
3503             /*
3504              * special case: StartIO-based writing must stay serialized, so just
3505              * re-use one packet.
3506              */
3507             pkt = DequeueFreeTransferPacket(Fdo, TRUE);
3508             if (pkt) {
3509                 SimplePushSlist(&pktList, (PSINGLE_LIST_ENTRY)&pkt->SlistEntry);
3510                 i = 1;
3511             } else {
3512                 i = 0;
3513             }
3514         } else {
3515             /*
3516              *  First get all the TRANSFER_PACKETs that we'll need at once.
3517              */
3518             for (i = 0; i < numPackets; i++){
3519                 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
3520                 if (pkt){
3521                     SimplePushSlist(&pktList, (PSINGLE_LIST_ENTRY)&pkt->SlistEntry);
3522                 }
3523                 else {
3524                     break;
3525                 }
3526             }
3527         }
3528 
3529 
3530         if ((i == numPackets) &&
3531             (!driverUsesStartIO)) {
3532             NTSTATUS pktStat;
3533 
3534             /*
3535              *  The IoStatus.Information field will be incremented to the
3536              *  transfer length as the pieces complete.
3537              */
3538             Irp->IoStatus.Information = 0;
3539 
3540             /*
3541              *  Store the number of transfer pieces inside the original IRP.
3542              *  It will be used to count down the pieces as they complete.
3543              */
3544             Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
3545 
3546             /*
3547              *  For the common 1-packet case, we want to allow for an optimization by BlkCache
3548              *  (and also potentially synchronous storage drivers) which may complete the
3549              *  downward request synchronously.
3550              *  In that synchronous completion case, we want to _not_ mark the original irp pending
3551              *  and thereby save on the top-level APC.
3552              *  It's critical to coordinate this with the completion routine so that we mark the original irp
3553              *  pending if-and-only-if we return STATUS_PENDING for it.
3554              */
3555             if (numPackets > 1){
3556                 IoMarkIrpPending(Irp);
3557                 status = STATUS_PENDING;
3558             }
3559             else {
3560                 status = STATUS_SUCCESS;
3561             }
3562 
3563             /*
3564              *  Transmit the pieces of the transfer.
3565              */
3566             while (entireXferLen > 0){
3567                 ULONG thisPieceLen = MIN(hwMaxXferLen, entireXferLen);
3568 
3569                 /*
3570                  *  Set up a TRANSFER_PACKET for this piece and send it.
3571                  */
3572                 slistEntry = SimplePopSlist(&pktList);
3573                 NT_ASSERT(slistEntry);
3574                 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
3575                 SetupReadWriteTransferPacket(   pkt,
3576                                             bufPtr,
3577                                             thisPieceLen,
3578                                             targetLocation,
3579                                             Irp);
3580 
3581                 //
3582                 // If the IRP needs to be split, then we need to use a partial MDL.
3583                 // This prevents problems if the same MDL is mapped multiple times.
3584                 //
3585                 if (numPackets > 1) {
3586                     pkt->UsePartialMdl = TRUE;
3587                 }
3588 
3589                 /*
3590                  *  When an IRP is completed, the completion routine checks to see if there
3591                  *  is a deferred IRP ready to sent down (assuming that there are no non-idle
3592                  *  requests waiting to be serviced). If such a deferred IRP is available, it
3593                  *  is sent down using this routine. However, if the lower driver completes
3594                  *  the request inline, there is a potential for multiple deferred IRPs being
3595                  *  sent down in the context of the same completion thread, thus exhausting
3596                  *  the call stack.
3597                  *  In order to prevent this from happening, we need to ensure that deferred
3598                  *  IRPs that are dequeued in the context of a request's completion routine
3599                  *  get posted to a DPC.
3600                  */
3601                 if (PostToDpc) {
3602 
3603                     pkt->RetryIn100nsUnits = 0;
3604                     TransferPacketQueueRetryDpc(pkt);
3605                     status = STATUS_PENDING;
3606 
3607                 } else {
3608 
3609                     pktStat = SubmitTransferPacket(pkt);
3610 
3611                     /*
3612                      *  If any of the packets completes with pending, we MUST return pending.
3613                      *  Also, if a packet completes with an error, return pending; this is because
3614                      *  in the completion routine we mark the original irp pending if the packet failed
3615                      *  (since we may retry, thereby switching threads).
3616                      */
3617                     if (pktStat != STATUS_SUCCESS){
3618                         status = STATUS_PENDING;
3619                     }
3620                 }
3621 
3622                 entireXferLen -= thisPieceLen;
3623                 bufPtr += thisPieceLen;
3624                 targetLocation.QuadPart += thisPieceLen;
3625             }
3626             NT_ASSERT(SimpleIsSlistEmpty(&pktList));
3627         }
3628         else if (i >= 1){
3629             /*
3630              *  We were unable to get all the TRANSFER_PACKETs we need,
3631              *  but we did get at least one.
3632              *  That means that we are in extreme low-memory stress.
3633              *  We'll try doing this transfer using a single packet.
3634              *  The port driver is certainly also in stress, so use one-page
3635              *  transfers.
3636              */
3637 
3638             /*
3639              *  Free all but one of the TRANSFER_PACKETs.
3640              */
3641             while (i-- > 1){
3642                 slistEntry = SimplePopSlist(&pktList);
3643                 NT_ASSERT(slistEntry);
3644                 pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
3645                 EnqueueFreeTransferPacket(Fdo, pkt);
3646             }
3647 
3648             /*
3649              *  Get the single TRANSFER_PACKET that we'll be using.
3650              */
3651             slistEntry = SimplePopSlist(&pktList);
3652             NT_ASSERT(slistEntry);
3653             NT_ASSERT(SimpleIsSlistEmpty(&pktList));
3654             pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
3655 
3656             if (!driverUsesStartIO) {
3657                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%p.", pkt));
3658             }
3659 
3660             /*
3661              *  Set the number of transfer packets (one)
3662              *  inside the original irp.
3663              */
3664             Irp->IoStatus.Information = 0;
3665             Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
3666             IoMarkIrpPending(Irp);
3667 
3668             /*
3669              *  Set up the TRANSFER_PACKET for a lowMem transfer and launch.
3670              */
3671             SetupReadWriteTransferPacket(  pkt,
3672                                         bufPtr,
3673                                         entireXferLen,
3674                                         targetLocation,
3675                                         Irp);
3676 
3677             InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
3678             StepLowMemRetry(pkt);
3679             status = STATUS_PENDING;
3680         }
3681         else {
3682             /*
3683              *  We were unable to get ANY TRANSFER_PACKETs.
3684              *  Defer this client irp until some TRANSFER_PACKETs free up.
3685              */
3686             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "No packets available in ServiceTransferRequest - deferring transfer (Irp=%p)...", Irp));
3687 
3688             if (priority == IoPagingPriorityHigh)
3689             {
3690                 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
3691 
3692                 if (fdoData->MaxInterleavedNormalIo < ClassMaxInterleavePerCriticalIo)
3693                 {
3694                     fdoData->MaxInterleavedNormalIo = 0;
3695                 }
3696                 else
3697                 {
3698                     fdoData->MaxInterleavedNormalIo -= ClassMaxInterleavePerCriticalIo;
3699                 }
3700 
3701                 fdoData->NumHighPriorityPagingIo--;
3702 
3703                 if (fdoData->NumHighPriorityPagingIo == 0)
3704                 {
3705                     LARGE_INTEGER period;
3706 
3707                     //
3708                     // Exiting throttle mode
3709                     //
3710 
3711                     KeQuerySystemTime(&fdoData->ThrottleStopTime);
3712 
3713                     period.QuadPart = fdoData->ThrottleStopTime.QuadPart - fdoData->ThrottleStartTime.QuadPart;
3714                     fdoData->LongestThrottlePeriod.QuadPart = max(fdoData->LongestThrottlePeriod.QuadPart, period.QuadPart);
3715                 }
3716 
3717                 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
3718             }
3719 
3720             deferClientIrp = TRUE;
3721         }
3722     }
3723     _Analysis_assume_(deferClientIrp);
3724     if (deferClientIrp)
3725     {
3726         IoMarkIrpPending(Irp);
3727         EnqueueDeferredClientIrp(Fdo, Irp);
3728         status = STATUS_PENDING;
3729     }
3730 
3731     NT_ASSERT(status != STATUS_UNSUCCESSFUL);
3732 
3733     return status;
3734 }
3735 
3736 
3737 /*++////////////////////////////////////////////////////////////////////////////
3738 
3739 ClassIoComplete()
3740 
3741 Routine Description:
3742 
3743     This routine executes when the port driver has completed a request.
3744     It looks at the SRB status in the completing SRB and if not success
3745     it checks for valid request sense buffer information. If valid, the
3746     info is used to update status with more precise message of type of
3747     error. This routine deallocates the SRB.
3748 
3749     This routine should only be placed on the stack location for a class
3750     driver FDO.
3751 
3752 Arguments:
3753 
3754     Fdo - Supplies the device object which represents the logical
3755         unit.
3756 
3757     Irp - Supplies the Irp which has completed.
3758 
3759     Context - Supplies a pointer to the SRB.
3760 
3761 Return Value:
3762 
3763     NT status
3764 
3765 --*/
3766 NTSTATUS
3767 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassIoComplete(IN PDEVICE_OBJECT Fdo,IN PIRP Irp,IN PVOID Context)3768 ClassIoComplete(
3769     IN PDEVICE_OBJECT Fdo,
3770     IN PIRP Irp,
3771     IN PVOID Context
3772     )
3773 {
3774     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
3775     PSCSI_REQUEST_BLOCK srb = Context;
3776     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
3777     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
3778     NTSTATUS status;
3779     BOOLEAN retry;
3780     BOOLEAN callStartNextPacket;
3781     ULONG srbFlags;
3782     ULONG srbFunction;
3783 
3784     NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
3785 
3786     if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
3787         srbFlags = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFlags;
3788         srbFunction = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction;
3789     } else {
3790         srbFlags = srb->SrbFlags;
3791         srbFunction = srb->Function;
3792     }
3793 
3794     #if DBG
3795         if (srbFunction == SRB_FUNCTION_FLUSH) {
3796             DBGLOGFLUSHINFO(fdoData, FALSE, FALSE, TRUE);
3797         }
3798     #endif
3799 
3800     //
3801     // Check SRB status for success of completing request.
3802     //
3803 
3804     if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
3805         LONGLONG retryInterval;
3806 
3807         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
3808 
3809         //
3810         // Release the queue if it is frozen.
3811         //
3812 
3813         if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
3814             ClassReleaseQueue(Fdo);
3815         }
3816         retry = InterpretSenseInfoWithoutHistory(
3817             Fdo,
3818             Irp,
3819             srb,
3820             irpStack->MajorFunction,
3821             ((irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) ?
3822              irpStack->Parameters.DeviceIoControl.IoControlCode :
3823              0),
3824             MAXIMUM_RETRIES -
3825                 ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
3826             &status,
3827             &retryInterval);
3828 
3829         //
3830         // For Persistent Reserve requests, make sure user gets back partial data.
3831         //
3832 
3833         if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL &&
3834             (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN ||
3835              irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_OUT) &&
3836             status == STATUS_DATA_OVERRUN) {
3837 
3838             status = STATUS_SUCCESS;
3839             retry = FALSE;
3840         }
3841 
3842         //
3843         // If the status is verified required and the this request
3844         // should bypass verify required then retry the request.
3845         //
3846 
3847         if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
3848             status == STATUS_VERIFY_REQUIRED) {
3849 
3850             status = STATUS_IO_DEVICE_ERROR;
3851             retry = TRUE;
3852         }
3853 
3854 #ifndef __REACTOS__
3855 #pragma warning(suppress:4213) // okay to cast Arg4 as a ulong for this use case
3856         if (retry && ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4)--) {
3857 #else
3858         if (retry && (*(ULONG *)&irpStack->Parameters.Others.Argument4)--) {
3859 #endif
3860 
3861 
3862             //
3863             // Retry request.
3864             //
3865 
3866             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,  "Retry request %p\n", Irp));
3867 
3868             if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
3869                 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
3870             }
3871 
3872             RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
3873             return STATUS_MORE_PROCESSING_REQUIRED;
3874         }
3875 
3876     } else {
3877 
3878         //
3879         // Set status for successful request
3880         //
3881         fdoData->LoggedTURFailureSinceLastIO = FALSE;
3882         ClasspPerfIncrementSuccessfulIo(fdoExtension);
3883         status = STATUS_SUCCESS;
3884     } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
3885 
3886 
3887     //
3888     // ensure we have returned some info, and it matches what the
3889     // original request wanted for PAGING operations only
3890     //
3891 
3892     if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
3893         NT_ASSERT(Irp->IoStatus.Information != 0);
3894         NT_ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
3895     }
3896 
3897     //
3898     // remember if the caller wanted to skip calling IoStartNextPacket.
3899     // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
3900     // calls.  this setting only affects device objects with StartIo routines.
3901     //
3902 
3903     callStartNextPacket = !TEST_FLAG(srbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
3904     if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
3905         callStartNextPacket = FALSE;
3906     }
3907 
3908     //
3909     // Free MDL if allocated.
3910     //
3911 
3912     if (TEST_FLAG(srbFlags, SRB_CLASS_FLAGS_FREE_MDL)) {
3913         SrbClearSrbFlags(srb, SRB_CLASS_FLAGS_FREE_MDL);
3914         IoFreeMdl(Irp->MdlAddress);
3915         Irp->MdlAddress = NULL;
3916     }
3917 
3918 
3919     //
3920     // Free the srb
3921     //
3922 
3923     if (!TEST_FLAG(srbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
3924 
3925         if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
3926             FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
3927         }
3928 
3929         if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
3930             ClassFreeOrReuseSrb(fdoExtension, srb);
3931         }
3932         else {
3933             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
3934             FREE_POOL(srb);
3935         }
3936 
3937     } else {
3938 
3939         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassIoComplete: Not Freeing srb @ %p because "
3940                     "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
3941         if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
3942             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassIoComplete: Not Freeing sensebuffer @ %p "
3943                         " because SRB_CLASS_FLAGS_PERSISTANT set\n",
3944                         srb->SenseInfoBuffer));
3945         }
3946 
3947     }
3948 
3949     //
3950     // Set status in completing IRP.
3951     //
3952 
3953     Irp->IoStatus.Status = status;
3954 
3955     //
3956     // Set the hard error if necessary.
3957     //
3958 
3959     if (!NT_SUCCESS(status) &&
3960         IoIsErrorUserInduced(status) &&
3961         (Irp->Tail.Overlay.Thread != NULL)
3962         ) {
3963 
3964         //
3965         // Store DeviceObject for filesystem, and clear
3966         // in IoStatus.Information field.
3967         //
3968 
3969         IoSetHardErrorOrVerifyDevice(Irp, Fdo);
3970         Irp->IoStatus.Information = 0;
3971     }
3972 
3973     //
3974     // If disk firmware update succeeded, log a system event.
3975     //
3976 
3977     if (NT_SUCCESS(status) &&
3978         (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) &&
3979         (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_FIRMWARE_ACTIVATE)) {
3980 
3981         ClasspLogSystemEventWithDeviceNumber(Fdo, IO_WARNING_DISK_FIRMWARE_UPDATED);
3982     }
3983 
3984     //
3985     // If pending has be returned for this irp then mark the current stack as
3986     // pending.
3987     //
3988 
3989     if (Irp->PendingReturned) {
3990         IoMarkIrpPending(Irp);
3991     }
3992 
3993     if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
3994         if (callStartNextPacket) {
3995             KIRQL oldIrql;
3996             KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
3997             IoStartNextPacket(Fdo, TRUE); // Yes, some IO must now be cancellable.
3998             KeLowerIrql(oldIrql);
3999         }
4000     }
4001 
4002     ClassReleaseRemoveLock(Fdo, Irp);
4003 
4004     return status;
4005 
4006 } // end ClassIoComplete()
4007 
4008 
4009 /*++////////////////////////////////////////////////////////////////////////////
4010 
4011 ClassSendSrbSynchronous()
4012 
4013 Routine Description:
4014 
4015     This routine is called by SCSI device controls to complete an
4016     SRB and send it to the port driver synchronously (ie wait for
4017     completion). The CDB is already completed along with the SRB CDB
4018     size and request timeout value.
4019 
4020 Arguments:
4021 
4022     Fdo - Supplies the functional device object which represents the target.
4023 
4024     Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
4025 
4026     BufferAddress - Supplies the address of the buffer.
4027 
4028     BufferLength - Supplies the length in bytes of the buffer.
4029 
4030     WriteToDevice - Indicates the data should be transfer to the device.
4031 
4032 Return Value:
4033 
4034     NTSTATUS indicating the final results of the operation.
4035 
4036     If NT_SUCCESS(), then the amount of usable data is contained in the field
4037        Srb->DataTransferLength
4038 
4039 --*/
4040 NTSTATUS
4041 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
4042 ClassSendSrbSynchronous(
4043     _In_ PDEVICE_OBJECT Fdo,
4044     _Inout_ PSCSI_REQUEST_BLOCK _Srb,
4045     _In_reads_bytes_opt_(BufferLength) PVOID BufferAddress,
4046     _In_ ULONG BufferLength,
4047     _In_ BOOLEAN WriteToDevice
4048     )
4049 {
4050 
4051     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
4052     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
4053     IO_STATUS_BLOCK ioStatus = {0};
4054     PIRP irp;
4055     PIO_STACK_LOCATION irpStack;
4056     KEVENT event;
4057     PVOID senseInfoBuffer = NULL;
4058     ULONG senseInfoBufferLength = SENSE_BUFFER_SIZE_EX;
4059     ULONG retryCount = MAXIMUM_RETRIES;
4060     NTSTATUS status;
4061     BOOLEAN retry;
4062     PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
4063 
4064     //
4065     // NOTE: This code is only pagable because we are not freezing
4066     //       the queue.  Allowing the queue to be frozen from a pagable
4067     //       routine could leave the queue frozen as we try to page in
4068     //       the code to unfreeze the queue.  The result would be a nice
4069     //       case of deadlock.  Therefore, since we are unfreezing the
4070     //       queue regardless of the result, just set the NO_FREEZE_QUEUE
4071     //       flag in the SRB.
4072     //
4073 
4074     NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
4075     NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
4076 
4077     if (Srb->Function != SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
4078         //
4079         // Write length to SRB.
4080         //
4081 
4082         Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
4083 
4084         //
4085         // Set SCSI bus address.
4086         //
4087 
4088         Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
4089     }
4090 
4091     //
4092     // The Srb->Function should have been set corresponding to SrbType.
4093     //
4094 
4095     NT_ASSERT( ((fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_SCSI_REQUEST_BLOCK) && (Srb->Function == SRB_FUNCTION_EXECUTE_SCSI)) ||
4096                ((fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) && (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK)) );
4097 
4098 
4099     //
4100     // Sense buffer is in aligned nonpaged pool.
4101     //
4102 
4103 #if defined(_ARM_) || defined(_ARM64_)
4104 
4105     //
4106     // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
4107     // based platforms. We are taking the conservative approach here.
4108     //
4109     senseInfoBufferLength = ALIGN_UP_BY(senseInfoBufferLength,KeGetRecommendedSharedDataAlignment());
4110 
4111 #endif
4112 
4113     senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
4114                                             senseInfoBufferLength,
4115                                             '7CcS');
4116 
4117     if (senseInfoBuffer == NULL) {
4118 
4119         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassSendSrbSynchronous: Can't allocate request sense "
4120                        "buffer\n"));
4121         return(STATUS_INSUFFICIENT_RESOURCES);
4122     }
4123 
4124 
4125     //
4126     // Enable auto request sense.
4127     //
4128     SrbSetSenseInfoBufferLength(Srb, SENSE_BUFFER_SIZE_EX);
4129     SrbSetSenseInfoBuffer(Srb, senseInfoBuffer);
4130 
4131     SrbSetDataBuffer(Srb, BufferAddress);
4132 
4133 
4134     //
4135     // Start retries here.
4136     //
4137 
4138 retry:
4139 
4140     //
4141     // use fdoextension's flags by default.
4142     // do not move out of loop, as the flag may change due to errors
4143     // sending this command.
4144     //
4145 
4146     SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
4147 
4148     if (BufferAddress != NULL) {
4149         if (WriteToDevice) {
4150             SrbSetSrbFlags(Srb, SRB_FLAGS_DATA_OUT);
4151         } else {
4152             SrbSetSrbFlags(Srb, SRB_FLAGS_DATA_IN);
4153         }
4154     }
4155 
4156 
4157     //
4158     // Initialize the QueueAction field.
4159     //
4160 
4161     SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
4162 
4163     //
4164     // Disable synchronous transfer for these requests.
4165     //
4166     SrbSetSrbFlags(Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
4167 
4168     //
4169     // Set the event object to the unsignaled state.
4170     // It will be used to signal request completion.
4171     //
4172 
4173     KeInitializeEvent(&event, NotificationEvent, FALSE);
4174 
4175     //
4176     // Build device I/O control request with METHOD_NEITHER data transfer.
4177     // We'll queue a completion routine to cleanup the MDL's and such ourself.
4178     //
4179 
4180     irp = IoAllocateIrp(
4181             (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
4182             FALSE);
4183 
4184     if (irp == NULL) {
4185         FREE_POOL(senseInfoBuffer);
4186         SrbSetSenseInfoBuffer(Srb, NULL);
4187         SrbSetSenseInfoBufferLength(Srb, 0);
4188         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassSendSrbSynchronous: Can't allocate Irp\n"));
4189         return(STATUS_INSUFFICIENT_RESOURCES);
4190     }
4191 
4192     //
4193     // Get next stack location.
4194     //
4195 
4196     irpStack = IoGetNextIrpStackLocation(irp);
4197 
4198     //
4199     // Set up SRB for execute scsi request. Save SRB address in next stack
4200     // for the port driver.
4201     //
4202 
4203     irpStack->MajorFunction = IRP_MJ_SCSI;
4204     irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;
4205 
4206     IoSetCompletionRoutine(irp,
4207                            ClasspSendSynchronousCompletion,
4208                            Srb,
4209                            TRUE,
4210                            TRUE,
4211                            TRUE);
4212 
4213     irp->UserIosb = &ioStatus;
4214     irp->UserEvent = &event;
4215 
4216     if (BufferAddress) {
4217         //
4218         // Build an MDL for the data buffer and stick it into the irp.  The
4219         // completion routine will unlock the pages and free the MDL.
4220         //
4221 
4222         irp->MdlAddress = IoAllocateMdl( BufferAddress,
4223                                          BufferLength,
4224                                          FALSE,
4225                                          FALSE,
4226                                          irp );
4227         if (irp->MdlAddress == NULL) {
4228             FREE_POOL(senseInfoBuffer);
4229             SrbSetSenseInfoBuffer(Srb, NULL);
4230             SrbSetSenseInfoBufferLength(Srb, 0);
4231             IoFreeIrp( irp );
4232             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassSendSrbSynchronous: Can't allocate MDL\n"));
4233             return STATUS_INSUFFICIENT_RESOURCES;
4234         }
4235 
4236         _SEH2_TRY {
4237 
4238             //
4239             // the io manager unlocks these pages upon completion
4240             //
4241 
4242             MmProbeAndLockPages( irp->MdlAddress,
4243                                  KernelMode,
4244                                  (WriteToDevice ? IoReadAccess :
4245                                                   IoWriteAccess));
4246 
4247 #ifdef _MSC_VER
4248         #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
4249 #endif
4250         } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
4251             status = _SEH2_GetExceptionCode();
4252 
4253             FREE_POOL(senseInfoBuffer);
4254             SrbSetSenseInfoBuffer(Srb, NULL);
4255             SrbSetSenseInfoBufferLength(Srb, 0);
4256             IoFreeMdl(irp->MdlAddress);
4257             IoFreeIrp(irp);
4258 
4259             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassSendSrbSynchronous: Exception %lx "
4260                            "locking buffer\n", status));
4261             _SEH2_YIELD(return status);
4262         } _SEH2_END;
4263     }
4264 
4265     //
4266     // Set the transfer length.
4267     //
4268 
4269     SrbSetDataTransferLength(Srb, BufferLength);
4270 
4271     //
4272     // Zero out status.
4273     //
4274 
4275     SrbSetScsiStatus(Srb, 0);
4276     Srb->SrbStatus = 0;
4277     SrbSetNextSrb(Srb, NULL);
4278 
4279     //
4280     // Set up IRP Address.
4281     //
4282 
4283     SrbSetOriginalRequest(Srb, irp);
4284 
4285     //
4286     // Call the port driver with the request and wait for it to complete.
4287     //
4288 
4289     status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
4290 
4291     if (status == STATUS_PENDING) {
4292         (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
4293         status = ioStatus.Status;
4294     }
4295 
4296 //    NT_ASSERT(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_PENDING);
4297     NT_ASSERT(status != STATUS_PENDING);
4298     NT_ASSERT(!(Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN));
4299 
4300     //
4301     // Clear the IRP address in SRB as IRP has been freed at this time
4302     // and don't want to leave any references that may be accessed.
4303     //
4304 
4305     SrbSetOriginalRequest(Srb, NULL);
4306 
4307     //
4308     // Check that request completed without error.
4309     //
4310 
4311     if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
4312 
4313         LONGLONG retryInterval;
4314 
4315         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb,
4316                     DBGGETSCSIOPSTR(Srb),
4317                     DBGGETSRBSTATUSSTR(Srb), (ULONG)Srb->SrbStatus, status,
4318                     DBGGETSENSECODESTR(Srb),
4319                     DBGGETADSENSECODESTR(Srb),
4320                     DBGGETADSENSEQUALIFIERSTR(Srb)));
4321 
4322         //
4323         // assert that the queue is not frozen
4324         //
4325 
4326         NT_ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
4327 
4328         //
4329         // Update status and determine if request should be retried.
4330         //
4331 
4332         retry = InterpretSenseInfoWithoutHistory(Fdo,
4333                                                  NULL, // no valid irp exists
4334                                                  (PSCSI_REQUEST_BLOCK)Srb,
4335                                                  IRP_MJ_SCSI,
4336                                                  0,
4337                                                  MAXIMUM_RETRIES  - retryCount,
4338                                                  &status,
4339                                                  &retryInterval);
4340 
4341         if (retry) {
4342 
4343             BOOLEAN validSense = FALSE;
4344             UCHAR additionalSenseCode = 0;
4345 
4346             if (status == STATUS_DEVICE_NOT_READY) {
4347 
4348                 validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
4349                                                      SrbGetSenseInfoBufferLength(Srb),
4350                                                      SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
4351                                                      NULL,
4352                                                      &additionalSenseCode,
4353                                                      NULL);
4354             }
4355 
4356             if ((validSense && additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) ||
4357                 (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
4358 
4359                 LARGE_INTEGER delay;
4360 
4361                 //
4362                 // Delay for at least 2 seconds.
4363                 //
4364 
4365                 if (retryInterval < 2*1000*1000*10) {
4366                     retryInterval = 2*1000*1000*10;
4367                 }
4368 
4369                 delay.QuadPart = -retryInterval;
4370 
4371                 //
4372                 // Stall for a while to let the device become ready
4373                 //
4374 
4375                 KeDelayExecutionThread(KernelMode, FALSE, &delay);
4376 
4377             }
4378 
4379 
4380             //
4381             // If retries are not exhausted then retry this operation.
4382             //
4383 
4384             if (retryCount--) {
4385 
4386                 if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
4387                     FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
4388                 }
4389 
4390                 goto retry;
4391             }
4392         }
4393 
4394 
4395     } else {
4396         fdoData->LoggedTURFailureSinceLastIO = FALSE;
4397         status = STATUS_SUCCESS;
4398     }
4399 
4400     //
4401     // required even though we allocated our own, since the port driver may
4402     // have allocated one also
4403     //
4404 
4405     if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
4406         FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
4407     }
4408 
4409     FREE_POOL(senseInfoBuffer);
4410     SrbSetSenseInfoBuffer(Srb, NULL);
4411     SrbSetSenseInfoBufferLength(Srb, 0);
4412 
4413     return status;
4414 }
4415 
4416 
4417 /*++////////////////////////////////////////////////////////////////////////////
4418 
4419 ClassInterpretSenseInfo()
4420 
4421 Routine Description:
4422 
4423     This routine interprets the data returned from the SCSI
4424     request sense. It determines the status to return in the
4425     IRP and whether this request can be retried.
4426 
4427 Arguments:
4428 
4429     Fdo - Supplies the device object associated with this request.
4430 
4431     Srb - Supplies the scsi request block which failed.
4432 
4433     MajorFunctionCode - Supplies the function code to be used for logging.
4434 
4435     IoDeviceCode - Supplies the device code to be used for logging.
4436 
4437     RetryCount - Number of times that the request has been retried.
4438 
4439     Status - Returns the status for the request.
4440 
4441     RetryInterval - Number of seconds before the request should be retried.
4442                     Zero indicates the request should be immediately retried.
4443 
4444 Return Value:
4445 
4446     BOOLEAN TRUE: Drivers should retry this request.
4447             FALSE: Drivers should not retry this request.
4448 
4449 --*/
4450 BOOLEAN
4451 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
4452 ClassInterpretSenseInfo(
4453     _In_ PDEVICE_OBJECT Fdo,
4454     _In_ PSCSI_REQUEST_BLOCK _Srb,
4455     _In_ UCHAR MajorFunctionCode,
4456     _In_ ULONG IoDeviceCode,
4457     _In_ ULONG RetryCount,
4458     _Out_ NTSTATUS *Status,
4459     _Out_opt_ _Deref_out_range_(0,100) ULONG *RetryInterval
4460     )
4461 {
4462     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
4463     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
4464     PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
4465     PVOID senseBuffer = SrbGetSenseInfoBuffer(Srb);
4466     BOOLEAN retry = TRUE;
4467     BOOLEAN logError = FALSE;
4468     BOOLEAN unhandledError = FALSE;
4469     BOOLEAN incrementErrorCount = FALSE;
4470 
4471     //
4472     // NOTE: This flag must be used only for read/write requests that
4473     // fail with a unexpected retryable error.
4474     //
4475     BOOLEAN logRetryableError = TRUE;
4476 
4477     //
4478     // Indicates if we should log this error in our internal log.
4479     //
4480     BOOLEAN logErrorInternal = TRUE;
4481 
4482     ULONGLONG badSector = 0;
4483     ULONG uniqueId = 0;
4484 
4485     NTSTATUS logStatus;
4486 
4487     ULONGLONG readSector;
4488     ULONG index;
4489 
4490     ULONG retryInterval = 0;
4491     KIRQL oldIrql;
4492     PCDB cdb = SrbGetCdb(Srb);
4493     UCHAR cdbOpcode = 0;
4494     ULONG cdbLength = SrbGetCdbLength(Srb);
4495 
4496 #if DBG
4497     BOOLEAN isReservationConflict = FALSE;
4498 #endif
4499 
4500     if (cdb) {
4501         cdbOpcode = cdb->CDB6GENERIC.OperationCode;
4502     }
4503 
4504     *Status = STATUS_IO_DEVICE_ERROR;
4505     logStatus = -1;
4506 
4507     if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
4508 
4509         //
4510         // Log anything remotely incorrect about paging i/o
4511         //
4512 
4513         logError = TRUE;
4514         uniqueId = 301;
4515         logStatus = IO_WARNING_PAGING_FAILURE;
4516     }
4517 
4518     //
4519     // Check that request sense buffer is valid.
4520     //
4521 
4522     NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
4523 
4524 
4525     //
4526     // must handle the SRB_STATUS_INTERNAL_ERROR case first,
4527     // as it has  all the flags set.
4528     //
4529 
4530     if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
4531 
4532         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
4533                     "ClassInterpretSenseInfo: Internal Error code is %x\n",
4534                     SrbGetSystemStatus(Srb)));
4535 
4536         retry = FALSE;
4537         *Status = SrbGetSystemStatus(Srb);
4538 
4539     } else if (SrbGetScsiStatus(Srb) == SCSISTAT_RESERVATION_CONFLICT) {
4540 
4541         //
4542         // Need to reserve STATUS_DEVICE_BUSY to convey reservation conflict
4543         // for read/write requests as there are upper level components that
4544         // have built-in assumptions that STATUS_DEVICE_BUSY implies reservation
4545         // conflict.
4546         //
4547         *Status = STATUS_DEVICE_BUSY;
4548         retry = FALSE;
4549         logError = FALSE;
4550 #if DBG
4551         isReservationConflict = TRUE;
4552 #endif
4553 
4554     } else {
4555 
4556         BOOLEAN validSense = FALSE;
4557 
4558         if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && senseBuffer)  {
4559 
4560             UCHAR errorCode = 0;
4561             UCHAR senseKey = 0;
4562             UCHAR addlSenseCode = 0;
4563             UCHAR addlSenseCodeQual = 0;
4564             BOOLEAN isIncorrectLengthValid = FALSE;
4565             BOOLEAN incorrectLength = FALSE;
4566             BOOLEAN isInformationValid = FALSE;
4567             ULONGLONG information = 0;
4568 
4569 
4570             validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
4571                                                  SrbGetSenseInfoBufferLength(Srb),
4572                                                  SCSI_SENSE_OPTIONS_NONE,
4573                                                  &senseKey,
4574                                                  &addlSenseCode,
4575                                                  &addlSenseCodeQual);
4576 
4577             if (!validSense && !IsSenseDataFormatValueValid(senseBuffer)) {
4578 
4579                 NT_ASSERT(FALSE);
4580 
4581                 validSense = ScsiGetFixedSenseKeyAndCodes(senseBuffer,
4582                                                           SrbGetSenseInfoBufferLength(Srb),
4583                                                           &senseKey,
4584                                                           &addlSenseCode,
4585                                                           &addlSenseCodeQual);
4586             }
4587 
4588             if (!validSense) {
4589                 goto __ClassInterpretSenseInfo_ProcessingInvalidSenseBuffer;
4590             }
4591 
4592             errorCode = ScsiGetSenseErrorCode(senseBuffer);
4593 
4594             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Error code is %x\n", errorCode));
4595             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Sense key is %x\n", senseKey));
4596             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Additional sense code is %x\n", addlSenseCode));
4597             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Additional sense code qualifier is %x\n", addlSenseCodeQual));
4598 
4599             if (IsDescriptorSenseDataFormat(senseBuffer)) {
4600 
4601                 //
4602                 // Sense data in Descriptor format
4603                 //
4604 
4605                 PVOID startBuffer = NULL;
4606                 UCHAR startBufferLength = 0;
4607 
4608 
4609                 if (ScsiGetSenseDescriptor(senseBuffer,
4610                                            SrbGetSenseInfoBufferLength(Srb),
4611                                            &startBuffer,
4612                                            &startBufferLength)) {
4613                     UCHAR outType;
4614                     PVOID outBuffer = NULL;
4615                     UCHAR outBufferLength = 0;
4616                     BOOLEAN foundBlockCommandType = FALSE;
4617                     BOOLEAN foundInformationType = FALSE;
4618                     UCHAR descriptorLength = 0;
4619 
4620                     UCHAR typeList[2] = {SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION,
4621                                          SCSI_SENSE_DESCRIPTOR_TYPE_BLOCK_COMMAND};
4622 
4623                     while ((!foundBlockCommandType || !foundInformationType) &&
4624                            ScsiGetNextSenseDescriptorByType(startBuffer,
4625                                                             startBufferLength,
4626                                                             typeList,
4627                                                             ARRAYSIZE(typeList),
4628                                                             &outType,
4629                                                             &outBuffer,
4630                                                             &outBufferLength)) {
4631 
4632                         descriptorLength = ScsiGetSenseDescriptorLength(outBuffer);
4633 
4634                         if (outBufferLength < descriptorLength) {
4635 
4636                             // Descriptor data is truncated.
4637                             // Complete searching descriptors. Exit the loop now.
4638                             break;
4639                         }
4640 
4641                         if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_BLOCK_COMMAND) {
4642 
4643                             //
4644                             // Block Command type
4645                             //
4646 
4647                             if (!foundBlockCommandType) {
4648 
4649                                 foundBlockCommandType = TRUE;
4650 
4651                                 if (ScsiValidateBlockCommandSenseDescriptor(outBuffer, outBufferLength)) {
4652                                     incorrectLength = ((PSCSI_SENSE_DESCRIPTOR_BLOCK_COMMAND)outBuffer)->IncorrectLength;
4653                                     isIncorrectLengthValid = TRUE;
4654                                 }
4655                             } else {
4656 
4657                                 //
4658                                 // A Block Command descriptor is already found earlier.
4659                                 //
4660                                 // T10 SPC specification only allows one descriptor for Block Command Descriptor type.
4661                                 // Assert here to catch devices that violate this rule. Ignore this descriptor.
4662                                 //
4663                                 NT_ASSERT(FALSE);
4664                             }
4665 
4666                         } else if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) {
4667 
4668                             //
4669                             // Information type
4670                             //
4671 
4672                             if (!foundInformationType) {
4673 
4674                                 foundInformationType = TRUE;
4675 
4676                                 if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) {
4677                                     REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information));
4678                                     isInformationValid = TRUE;
4679                                 }
4680                             } else {
4681 
4682                                 //
4683                                 // A Information descriptor is already found earlier.
4684                                 //
4685                                 // T10 SPC specification only allows one descriptor for Information Descriptor type.
4686                                 // Assert here to catch devices that violate this rule. Ignore this descriptor.
4687                                 //
4688                                 NT_ASSERT(FALSE);
4689                             }
4690 
4691                         } else {
4692 
4693                             //
4694                             // ScsiGetNextDescriptorByType should only return a type that is specified by us.
4695                             //
4696                             NT_ASSERT(FALSE);
4697                             break;
4698                         }
4699 
4700                         //
4701                         // Advance to start address of next descriptor
4702                         //
4703                         startBuffer = (PUCHAR)outBuffer + descriptorLength;
4704                         startBufferLength = outBufferLength - descriptorLength;
4705                     }
4706                 }
4707             } else {
4708 
4709                 //
4710                 // Sense data in Fixed format
4711                 //
4712 
4713                 incorrectLength = ((PFIXED_SENSE_DATA)(senseBuffer))->IncorrectLength;
4714                 REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseBuffer)->Information));
4715                 isInformationValid = TRUE;
4716                 isIncorrectLengthValid = TRUE;
4717             }
4718 
4719 
4720             switch (senseKey) {
4721 
4722                 case SCSI_SENSE_NO_SENSE: {
4723 
4724                     //
4725                     // Check other indicators.
4726                     //
4727 
4728                     if (isIncorrectLengthValid && incorrectLength) {
4729 
4730                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
4731                                     "Incorrect length detected.\n"));
4732                         *Status = STATUS_INVALID_BLOCK_LENGTH ;
4733                         retry   = FALSE;
4734 
4735                     } else {
4736 
4737                         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
4738                                     "No specific sense key\n"));
4739                         *Status = STATUS_IO_DEVICE_ERROR;
4740                         retry   = TRUE;
4741                     }
4742 
4743                     break;
4744                 } // end SCSI_SENSE_NO_SENSE
4745 
4746                 case SCSI_SENSE_RECOVERED_ERROR: {
4747 
4748                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
4749                                 "Recovered error\n"));
4750                     *Status = STATUS_SUCCESS;
4751                     retry = FALSE;
4752                     logError = TRUE;
4753                     uniqueId = 258;
4754 
4755                     switch(addlSenseCode) {
4756                         case SCSI_ADSENSE_TRACK_ERROR:
4757                         case SCSI_ADSENSE_SEEK_ERROR: {
4758                             logStatus = IO_ERR_SEEK_ERROR;
4759                             break;
4760                         }
4761 
4762                         case SCSI_ADSENSE_REC_DATA_NOECC:
4763                         case SCSI_ADSENSE_REC_DATA_ECC: {
4764                             logStatus = IO_RECOVERED_VIA_ECC;
4765                             break;
4766                         }
4767 
4768                         case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
4769 
4770                             UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
4771 
4772                             *((PULONG)wmiEventData) = sizeof(UCHAR);
4773                             wmiEventData[sizeof(ULONG)] = addlSenseCodeQual;
4774 
4775                             //
4776                             // Don't log another eventlog if we have already logged once
4777                             // NOTE: this should have been interlocked, but the structure
4778                             //       was publicly defined to use a BOOLEAN (char).  Since
4779                             //       media only reports these errors once per X minutes,
4780                             //       the potential race condition is nearly non-existant.
4781                             //       the worst case is duplicate log entries, so ignore.
4782                             //
4783 
4784                             logError = FALSE;
4785                             if (fdoExtension->FailurePredicted == 0) {
4786                                 logError = TRUE;
4787                             }
4788                             fdoExtension->FailureReason = addlSenseCodeQual;
4789                             logStatus = IO_WRN_FAILURE_PREDICTED;
4790 
4791                             ClassNotifyFailurePredicted(fdoExtension,
4792                                                         (PUCHAR)wmiEventData,
4793                                                         sizeof(wmiEventData),
4794                                                         FALSE,   // do not log error
4795                                                         4,          // unique error value
4796                                                         SrbGetPathId(Srb),
4797                                                         SrbGetTargetId(Srb),
4798                                                         SrbGetLun(Srb));
4799 
4800                             fdoExtension->FailurePredicted = TRUE;
4801                             break;
4802                         }
4803 
4804                         default: {
4805                             logStatus = IO_ERR_CONTROLLER_ERROR;
4806                             break;
4807                         }
4808 
4809                     } // end switch(addlSenseCode)
4810 
4811                     if (isIncorrectLengthValid && incorrectLength) {
4812 
4813                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
4814                                     "Incorrect length detected.\n"));
4815                         *Status = STATUS_INVALID_BLOCK_LENGTH ;
4816                     }
4817 
4818 
4819                     break;
4820                 } // end SCSI_SENSE_RECOVERED_ERROR
4821 
4822                 case SCSI_SENSE_NOT_READY: {
4823 
4824                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4825                                 "Device not ready\n"));
4826                     *Status = STATUS_DEVICE_NOT_READY;
4827 
4828                     switch (addlSenseCode) {
4829 
4830                         case SCSI_ADSENSE_LUN_NOT_READY: {
4831 
4832                             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4833                                         "Lun not ready\n"));
4834 
4835                             retryInterval = NOT_READY_RETRY_INTERVAL;
4836 
4837                             switch (addlSenseCodeQual) {
4838 
4839                                 case SCSI_SENSEQ_BECOMING_READY: {
4840                                     DEVICE_EVENT_BECOMING_READY notReady = {0};
4841 
4842                                     logRetryableError = FALSE;
4843                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4844                                                 "In process of becoming ready\n"));
4845 
4846                                     notReady.Version = 1;
4847                                     notReady.Reason = 1;
4848                                     notReady.Estimated100msToReady = retryInterval * 10;
4849                                     ClassSendNotification(fdoExtension,
4850                                                            &GUID_IO_DEVICE_BECOMING_READY,
4851                                                            sizeof(DEVICE_EVENT_BECOMING_READY),
4852                                                            &notReady);
4853                                     break;
4854                                 }
4855 
4856                                 case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
4857                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4858                                                 "Manual intervention required\n"));
4859                                     *Status = STATUS_NO_MEDIA_IN_DEVICE;
4860                                     retry = FALSE;
4861                                     break;
4862                                 }
4863 
4864                                 case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
4865                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4866                                                 "Format in progress\n"));
4867                                     retry = FALSE;
4868                                     break;
4869                                 }
4870 
4871                                 case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
4872                                     DEVICE_EVENT_BECOMING_READY notReady = {0};
4873 
4874                                     logRetryableError = FALSE;
4875                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4876                                                 "Operation In Progress\n"));
4877 
4878                                     notReady.Version = 1;
4879                                     notReady.Reason = 2;
4880                                     notReady.Estimated100msToReady = retryInterval * 10;
4881                                     ClassSendNotification(fdoExtension,
4882                                                            &GUID_IO_DEVICE_BECOMING_READY,
4883                                                            sizeof(DEVICE_EVENT_BECOMING_READY),
4884                                                            &notReady);
4885 
4886                                     break;
4887                                 }
4888 
4889                                 case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
4890                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4891                                                 "Long write in progress\n"));
4892                                     //
4893                                     // This has been seen as a transcient failure on some cdrom
4894                                     // drives. The cdrom class driver is going to override this
4895                                     // setting but has no way of dropping the retry interval
4896                                     //
4897                                     retry = FALSE;
4898                                     retryInterval = 1;
4899                                     break;
4900                                 }
4901 
4902                                 case SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS: {
4903                                     logRetryableError = FALSE;
4904                                     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4905                                                 "The device (%p) is busy allocating space.\n",
4906                                                 Fdo));
4907 
4908                                     //
4909                                     // This indicates that a thinly-provisioned device has hit
4910                                     // a temporary resource exhaustion and is busy allocating
4911                                     // more space.  We need to retry the request as the device
4912                                     // will eventually be able to service it.
4913                                     //
4914                                     *Status = STATUS_RETRY;
4915                                     retry = TRUE;
4916 
4917                                     break;
4918                                 }
4919 
4920                                 case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
4921 
4922                                     if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
4923                                                    CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
4924 
4925                                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
4926                                                     "ClassInterpretSenseInfo: "
4927                                                     "not ready, cause unknown\n"));
4928                                         /*
4929                                         Many non-WHQL certified drives (mostly CD-RW) return
4930                                         this when they have no media instead of the obvious
4931                                         choice of:
4932 
4933                                         SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
4934 
4935                                         These drives should not pass WHQL certification due
4936                                         to this discrepency.
4937 
4938                                         */
4939                                         retry = FALSE;
4940                                         break;
4941 
4942                                     } else {
4943 
4944                                         //
4945                                         // Treat this as init command required and fall through.
4946                                         //
4947                                     }
4948                                 }
4949 
4950                                 case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
4951                                 default: {
4952                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4953                                                 "Initializing command required\n"));
4954                                     retryInterval = 0; // go back to default
4955                                     logRetryableError = FALSE;
4956 
4957                                     //
4958                                     // This sense code/additional sense code
4959                                     // combination may indicate that the device
4960                                     // needs to be started.  Send an start unit if this
4961                                     // is a disk device.
4962                                     //
4963                                     if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT) &&
4964                                         !TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_LOW_PRIORITY)){
4965 
4966                                         ClassSendStartUnit(Fdo);
4967                                     }
4968                                     break;
4969                                 }
4970 
4971                             } // end switch (addlSenseCodeQual)
4972                             break;
4973                         }
4974 
4975                         case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
4976                             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4977                                         "No Media in device.\n"));
4978                             *Status = STATUS_NO_MEDIA_IN_DEVICE;
4979                             retry = FALSE;
4980 
4981                             //
4982                             // signal MCN that there isn't any media in the device
4983                             //
4984                             if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
4985                                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
4986                                             "No Media in a non-removable device %p\n",
4987                                             Fdo));
4988                             }
4989 
4990                             if (addlSenseCodeQual == 0xCC){
4991                                 /*
4992                                  *  The IMAPI filter returns this ASCQ value when it is burning CD-R media.
4993                                  *  We want to indicate that the media is not present to most applications;
4994                                  *  but RSM has to know that the media is still in the drive (i.e. the drive is not free).
4995                                  */
4996                                 ClassSetMediaChangeState(fdoExtension, MediaUnavailable, FALSE);
4997                             }
4998                             else {
4999                                 ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
5000                             }
5001 
5002                             break;
5003                         }
5004                     } // end switch (addlSenseCode)
5005 
5006                     break;
5007                 } // end SCSI_SENSE_NOT_READY
5008 
5009                 case SCSI_SENSE_MEDIUM_ERROR: {
5010                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5011                                 "Medium Error (bad block)\n"));
5012                     *Status = STATUS_DEVICE_DATA_ERROR;
5013 
5014                     retry = FALSE;
5015                     logError = TRUE;
5016                     uniqueId = 256;
5017                     logStatus = IO_ERR_BAD_BLOCK;
5018 
5019                     //
5020                     // Check if this error is due to unknown format
5021                     //
5022                     if (addlSenseCode == SCSI_ADSENSE_INVALID_MEDIA) {
5023 
5024                         switch (addlSenseCodeQual) {
5025 
5026                             case SCSI_SENSEQ_UNKNOWN_FORMAT: {
5027 
5028                                 *Status = STATUS_UNRECOGNIZED_MEDIA;
5029 
5030                                 //
5031                                 // Log error only if this is a paging request
5032                                 //
5033                                 if (!TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
5034                                     logError = FALSE;
5035                                 }
5036                                 break;
5037                             }
5038 
5039                             case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
5040 
5041                                 *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
5042                                 logError = FALSE;
5043                                 break;
5044 
5045                             }
5046                             default: {
5047                                 break;
5048                             }
5049                         } // end switch addlSenseCodeQual
5050 
5051                     } // end SCSI_ADSENSE_INVALID_MEDIA
5052 
5053                     break;
5054 
5055                 } // end SCSI_SENSE_MEDIUM_ERROR
5056 
5057                 case SCSI_SENSE_HARDWARE_ERROR: {
5058                     BOOLEAN logHardwareError = TRUE;
5059 
5060                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5061                                 "Hardware error\n"));
5062 
5063                     if (fdoData->LegacyErrorHandling == FALSE) {
5064                         //
5065                         // Hardware errors indicate something has seriously gone
5066                         // wrong with the device and retries are very unlikely to
5067                         // succeed so fail this request back immediately.
5068                         //
5069                         retry = FALSE;
5070                         *Status = STATUS_DEVICE_HARDWARE_ERROR;
5071                         logError = FALSE;
5072 
5073                     } else {
5074                         //
5075                         // Revert to legacy behavior.  That is, retry everything by default.
5076                         //
5077                         retry = TRUE;
5078                         *Status = STATUS_IO_DEVICE_ERROR;
5079                         logError = TRUE;
5080                         uniqueId = 257;
5081                         logStatus = IO_ERR_CONTROLLER_ERROR;
5082                         logHardwareError = FALSE;
5083 
5084                         //
5085                         // This indicates the possibility of a dropped FC packet.
5086                         //
5087                         if ((addlSenseCode == SCSI_ADSENSE_LOGICAL_UNIT_ERROR && addlSenseCodeQual == SCSI_SENSEQ_TIMEOUT_ON_LOGICAL_UNIT) ||
5088                             (addlSenseCode == SCSI_ADSENSE_DATA_TRANSFER_ERROR && addlSenseCodeQual == SCSI_SENSEQ_INITIATOR_RESPONSE_TIMEOUT)) {
5089                             //
5090                             // Fail requests that report this error back to the application.
5091                             //
5092                             retry = FALSE;
5093 
5094                             //
5095                             // Log a more descriptive error and avoid a second
5096                             // error message (IO_ERR_CONTROLLER_ERROR) being logged.
5097                             //
5098                             logHardwareError = TRUE;
5099                             logError = FALSE;
5100                         }
5101                     }
5102 
5103                     //
5104                     // If CRC error was returned, retry after a slight delay.
5105                     //
5106                     if (addlSenseCode == SCSI_ADSENSE_LUN_COMMUNICATION &&
5107                         addlSenseCodeQual == SCSI_SESNEQ_COMM_CRC_ERROR) {
5108                         retry = TRUE;
5109                         retryInterval = 1;
5110                         logHardwareError = FALSE;
5111                         logError = FALSE;
5112                     }
5113 
5114                     //
5115                     // Hardware errors warrant a more descriptive error.
5116                     // Specifically, we need to ensure this disk is easily
5117                     // identifiable.
5118                     //
5119                     if (logHardwareError) {
5120                         UCHAR senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
5121                         UCHAR senseBufferSize = 0;
5122 
5123                         if (ScsiGetTotalSenseByteCountIndicated(senseBuffer,
5124                                                                 senseInfoBufferLength,
5125                                                                 &senseBufferSize)) {
5126 
5127                             senseBufferSize = min(senseBufferSize, senseInfoBufferLength);
5128 
5129                         } else {
5130                             //
5131                             // it's smaller than required to read the total number of
5132                             // valid bytes, so just use the SenseInfoBufferLength field.
5133                             //
5134                             senseBufferSize = senseInfoBufferLength;
5135                         }
5136 
5137                         ClasspQueueLogIOEventWithContextWorker(Fdo,
5138                                                                senseBufferSize,
5139                                                                senseBuffer,
5140                                                                SRB_STATUS(Srb->SrbStatus),
5141                                                                SrbGetScsiStatus(Srb),
5142                                                                (ULONG)IO_ERROR_IO_HARDWARE_ERROR,
5143                                                                cdbLength,
5144                                                                cdb,
5145                                                                NULL);
5146                     }
5147 
5148                     break;
5149                 } // end SCSI_SENSE_HARDWARE_ERROR
5150 
5151                 case SCSI_SENSE_ILLEGAL_REQUEST: {
5152 
5153                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5154                                 "Illegal SCSI request\n"));
5155                     *Status = STATUS_INVALID_DEVICE_REQUEST;
5156                     retry = FALSE;
5157 
5158                     switch (addlSenseCode) {
5159 
5160                         case SCSI_ADSENSE_NO_SENSE: {
5161 
5162                             switch (addlSenseCodeQual) {
5163 
5164                                 //
5165                                 // 1. Duplicate List Identifier
5166                                 //
5167                                 case SCSI_SENSEQ_OPERATION_IS_IN_PROGRESS: {
5168 
5169                                     //
5170                                     // XCOPY, READ BUFFER and CHANGE ALIASES return this sense combination under
5171                                     // certain conditions. Since these commands aren't sent down natively by the
5172                                     // Windows OS, return the default error for them and only handle this sense
5173                                     // combination for offload data transfer commands.
5174                                     //
5175                                     if (ClasspIsOffloadDataTransferCommand(cdb)) {
5176 
5177                                         TracePrint((TRACE_LEVEL_ERROR,
5178                                                     TRACE_FLAG_IOCTL,
5179                                                     "ClassInterpretSenseInfo (%p): Duplicate List Identifier (command %x, parameter field offset 0x%016llx)\n",
5180                                                     Fdo,
5181                                                     cdbOpcode,
5182                                                     information));
5183 
5184                                         NT_ASSERTMSG("Duplicate list identifier specified", FALSE);
5185 
5186                                         //
5187                                         // The host should ensure that it uses unique list id for each TokenOperation request.
5188                                         //
5189                                         *Status = STATUS_OPERATION_IN_PROGRESS;
5190                                     }
5191                                     break;
5192                                 }
5193                             }
5194                             break;
5195                         }
5196 
5197                         case SCSI_ADSENSE_LUN_COMMUNICATION: {
5198 
5199                             switch (addlSenseCodeQual) {
5200 
5201                                 //
5202                                 // 1. Source/Destination pairing can't communicate with each other or the copy manager.
5203                                 //
5204                                 case SCSI_SENSEQ_UNREACHABLE_TARGET: {
5205 
5206                                     TracePrint((TRACE_LEVEL_ERROR,
5207                                                 TRACE_FLAG_IOCTL,
5208                                                 "ClassInterpretSenseInfo (%p): Source-Destination LUNs can't communicate (command %x)\n",
5209                                                 Fdo,
5210                                                 cdbOpcode));
5211 
5212                                     *Status = STATUS_DEVICE_UNREACHABLE;
5213                                     break;
5214                                 }
5215                             }
5216                             break;
5217                         }
5218 
5219                         case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
5220 
5221                             switch (addlSenseCodeQual) {
5222 
5223                                 //
5224                                 // 1. Sum of logical block fields in all block device range descriptors is greater than number
5225                                 //    of logical blocks in the ROD minus block offset into ROD
5226                                 //
5227                                 case SCSI_SENSEQ_DATA_UNDERRUN: {
5228 
5229                                     TracePrint((TRACE_LEVEL_ERROR,
5230                                                 TRACE_FLAG_IOCTL,
5231                                                 "ClassInterpretSenseInfo (%p): Host specified a transfer length greater than what is represented by the token (considering the offset) [command %x]\n",
5232                                                 Fdo,
5233                                                 cdbOpcode));
5234 
5235                                     NT_ASSERTMSG("Host specified blocks to write beyond what is represented by the token", FALSE);
5236 
5237                                     *Status = STATUS_DATA_OVERRUN;
5238                                     break;
5239                                 }
5240                             }
5241                             break;
5242                         }
5243 
5244                         //
5245                         // 1. Parameter data truncation (e.g. last descriptor was not fully specified)
5246                         //
5247                         case SCSI_ADSENSE_PARAMETER_LIST_LENGTH: {
5248 
5249                             TracePrint((TRACE_LEVEL_ERROR,
5250                                         TRACE_FLAG_IOCTL,
5251                                         "ClassInterpretSenseInfo (%p): Target truncated the block device range descriptors in the parameter list (command %x)\n",
5252                                         Fdo,
5253                                         cdbOpcode));
5254 
5255                             NT_ASSERTMSG("Parameter data truncation", FALSE);
5256 
5257                             *Status = STATUS_DATA_OVERRUN;
5258                             break;
5259                         }
5260 
5261                         case SCSI_ADSENSE_ILLEGAL_COMMAND: {
5262                             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5263                                         "Illegal command\n"));
5264                             break;
5265                         }
5266 
5267                         case SCSI_ADSENSE_ILLEGAL_BLOCK: {
5268 
5269                             LARGE_INTEGER logicalBlockAddr;
5270                             LARGE_INTEGER lastLBA;
5271                             ULONG numTransferBlocks = 0;
5272 
5273                             logicalBlockAddr.QuadPart = 0;
5274 
5275                             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Illegal block address\n"));
5276 
5277                             *Status = STATUS_NONEXISTENT_SECTOR;
5278 
5279                             if (Fdo->DeviceType == FILE_DEVICE_DISK) {
5280 
5281                                 if (IS_SCSIOP_READWRITE(cdbOpcode) && cdb) {
5282 
5283                                     if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
5284                                         REVERSE_BYTES_QUAD(&logicalBlockAddr, &cdb->CDB16.LogicalBlock);
5285                                         REVERSE_BYTES(&numTransferBlocks, &cdb->CDB16.TransferLength);
5286                                     } else {
5287                                         REVERSE_BYTES(&logicalBlockAddr.LowPart, &cdb->CDB10.LogicalBlockByte0);
5288                                         REVERSE_BYTES_SHORT((PUSHORT)&numTransferBlocks, &cdb->CDB10.TransferBlocksMsb);
5289                                     }
5290 
5291                                     REVERSE_BYTES_QUAD(&lastLBA, &fdoData->LastKnownDriveCapacityData.LogicalBlockAddress);
5292 
5293                                     if ((logicalBlockAddr.QuadPart > lastLBA.QuadPart) ||
5294                                         ((logicalBlockAddr.QuadPart + numTransferBlocks - 1) > lastLBA.QuadPart)) {
5295 
5296                                         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5297                                                     "Request beyond boundary. Last LBA: 0x%I64X Read LBA: 0x%I64X Length: 0x%X\n",
5298                                                     (__int64) lastLBA.QuadPart, (__int64) logicalBlockAddr.QuadPart, numTransferBlocks));
5299                                     } else {
5300                                         //
5301                                         // Should only retry these if the request was
5302                                         // truly within our expected size.
5303                                         //
5304                                         // Fujitsu IDE drives have been observed to
5305                                         // return this error transiently for a legal LBA;
5306                                         // manual retry in the debugger then works, so
5307                                         // there is a good chance that a programmed retry
5308                                         // will also work.
5309                                         //
5310 
5311                                         retry = TRUE;
5312                                         retryInterval = 5;
5313                                     }
5314                                 } else if (ClasspIsOffloadDataTransferCommand(cdb)) {
5315 
5316                                     //
5317                                     // 1. Number of logical blocks of block device range descriptor exceeds capacity of the medium
5318                                     //
5319                                     TracePrint((TRACE_LEVEL_ERROR,
5320                                                 TRACE_FLAG_IOCTL,
5321                                                 "ClassInterpretSenseInfo (%p): LBA out of range (command %x, parameter field offset 0x%016llx)\n",
5322                                                 Fdo,
5323                                                 cdbOpcode,
5324                                                 information));
5325 
5326                                     NT_ASSERTMSG("Number of blocks specified exceeds LUN capacity", FALSE);
5327                                 }
5328                             }
5329                             break;
5330                         }
5331 
5332                         //
5333                         //  1. Generic error - cause not reportable
5334                         //  2. Insufficient resources to create ROD
5335                         //  3. Insufficient resources to create Token
5336                         //  4. Max number of tokens exceeded
5337                         //  5. Remote Token creation not supported
5338                         //  6. Token expired
5339                         //  7. Token unknown
5340                         //  8. Unsupported Token type
5341                         //  9. Token corrupt
5342                         // 10. Token revoked
5343                         // 11. Token cancelled
5344                         // 12. Remote Token usage not supported
5345                         //
5346                         case SCSI_ADSENSE_INVALID_TOKEN: {
5347 
5348                             TracePrint((TRACE_LEVEL_ERROR,
5349                                         TRACE_FLAG_IOCTL,
5350                                         "ClassInterpretSenseInfo (%p): Invalid/Expired/Modified token specified (command %x, parameter field offset 0x%016llx)\n",
5351                                         Fdo,
5352                                         cdbOpcode,
5353                                         information));
5354 
5355                             *Status = STATUS_INVALID_TOKEN;
5356                             break;
5357                         }
5358 
5359                         case SCSI_ADSENSE_INVALID_CDB: {
5360                             if (ClasspIsOffloadDataTransferCommand(cdb)) {
5361 
5362                                 //
5363                                 // 1. Mismatched I_T nexus and list identifier
5364                                 //
5365                                 TracePrint((TRACE_LEVEL_ERROR,
5366                                             TRACE_FLAG_IOCTL,
5367                                             "ClassInterpretSenseInfo (%p): Incorrect I_T nexus likely used (command %x)\n",
5368                                             Fdo,
5369                                             cdbOpcode));
5370 
5371                                 //
5372                                 // The host should ensure that it sends TokenOperation and ReceiveTokenInformation for the same
5373                                 // list Id using the same I_T nexus.
5374                                 //
5375                                 *Status = STATUS_INVALID_INITIATOR_TARGET_PATH;
5376 
5377                             } else {
5378 
5379                                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5380                                             "Invalid CDB\n"));
5381 
5382                                 //
5383                                 // Note: the retry interval is not typically used.
5384                                 // it is set here only because a ClassErrorHandler
5385                                 // cannot set the retryInterval, and the error may
5386                                 // require a few commands to be sent to clear whatever
5387                                 // caused this condition (i.e. disk clears the write
5388                                 // cache, requiring at least two commands)
5389                                 //
5390                                 // hopefully, this shortcoming can be changed for
5391                                 // blackcomb.
5392                                 //
5393 
5394                                 retryInterval = 3;
5395                             }
5396                             break;
5397                         }
5398 
5399                         case SCSI_ADSENSE_INVALID_LUN: {
5400                             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5401                                         "Invalid LUN\n"));
5402                             *Status = STATUS_NO_SUCH_DEVICE;
5403                             break;
5404                         }
5405 
5406                         case SCSI_ADSENSE_INVALID_FIELD_PARAMETER_LIST: {
5407 
5408                             switch (addlSenseCodeQual) {
5409 
5410                                 //
5411                                 // 1. Alignment violation (e.g. copy manager is unable to copy because destination offset is NOT aligned to LUN's granularity/alignment)
5412                                 //
5413                                 case SCSI_SENSEQ_INVALID_RELEASE_OF_PERSISTENT_RESERVATION: {
5414 
5415                                     TracePrint((TRACE_LEVEL_ERROR,
5416                                                 TRACE_FLAG_IOCTL,
5417                                                 "ClassInterpretSenseInfo (%p): Alignment violation for command %x.\n",
5418                                                 Fdo,
5419                                                 cdbOpcode));
5420 
5421                                     NT_ASSERTMSG("Specified offset is not aligned to LUN's granularity", FALSE);
5422 
5423                                     *Status = STATUS_INVALID_OFFSET_ALIGNMENT;
5424                                     break;
5425                                 }
5426 
5427                                 //
5428                                 // 1. Number of block device range descriptors is greater than maximum range descriptors
5429                                 //
5430                                 case SCSI_SENSEQ_TOO_MANY_SEGMENT_DESCRIPTORS: {
5431 
5432                                     TracePrint((TRACE_LEVEL_ERROR,
5433                                                 TRACE_FLAG_IOCTL,
5434                                                 "ClassInterpretSenseInfo (%p): Too many descriptors in parameter list for command %x (parameter field offset 0x%016llx)\n",
5435                                                 Fdo,
5436                                                 cdbOpcode,
5437                                                 information));
5438 
5439                                     NT_ASSERTMSG("Too many descriptors specified", FALSE);
5440 
5441                                     *Status = STATUS_TOO_MANY_SEGMENT_DESCRIPTORS;
5442                                     break;
5443                                 }
5444 
5445                                 default: {
5446 
5447                                     if (ClasspIsOffloadDataTransferCommand(cdb)) {
5448 
5449                                         //
5450                                         // 1. (Various) Invalid parameter length
5451                                         // 2. Requested inactivity timeout is greater than maximum inactivity timeout
5452                                         // 3. Same LBA is included in more than one block device range descriptor (overlapping LBAs)
5453                                         // 4. Total number of logical blocks of all block range descriptors is greater than the maximum transfer size
5454                                         // 5. Total number of logical blocks of all block range descriptors is greater than maximum token transfer size
5455                                         //    (e.g. WriteUsingToken descriptors specify a cumulative total block count that exceeds the PopulateToken that created the token)
5456                                         // 6. Block offset into ROD specified an offset that is greater than or equal to the number of logical blocks in the ROD
5457                                         // 7. Number of logical blocks in a block device range descriptor is greater than maximum transfer length in blocks
5458                                         //
5459                                         TracePrint((TRACE_LEVEL_ERROR,
5460                                                     TRACE_FLAG_IOCTL,
5461                                                     "ClassInterpretSenseInfo (%p): Illegal field in parameter list for command %x (parameter field offset 0x%016llx) [AddSense %x, AddSenseQ %x]\n",
5462                                                     Fdo,
5463                                                     cdbOpcode,
5464                                                     information,
5465                                                     addlSenseCode,
5466                                                     addlSenseCodeQual));
5467 
5468                                         NT_ASSERTMSG("Invalid field in parameter list", FALSE);
5469 
5470                                         *Status = STATUS_INVALID_FIELD_IN_PARAMETER_LIST;
5471                                     }
5472 
5473                                     break;
5474                                 }
5475                             }
5476                             break;
5477                         }
5478 
5479                         case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
5480                             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5481                                         "Copy protection failure\n"));
5482 
5483                             *Status = STATUS_COPY_PROTECTION_FAILURE;
5484 
5485                             switch (addlSenseCodeQual) {
5486                                 case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
5487                                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5488                                                 "ClassInterpretSenseInfo: "
5489                                                 "Authentication failure\n"));
5490                                     *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
5491                                     break;
5492                                 case SCSI_SENSEQ_KEY_NOT_PRESENT:
5493                                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5494                                                 "ClassInterpretSenseInfo: "
5495                                                 "Key not present\n"));
5496                                     *Status = STATUS_CSS_KEY_NOT_PRESENT;
5497                                     break;
5498                                 case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
5499                                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5500                                                 "ClassInterpretSenseInfo: "
5501                                                 "Key not established\n"));
5502                                     *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
5503                                     break;
5504                                 case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
5505                                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5506                                                 "ClassInterpretSenseInfo: "
5507                                                 "Read of scrambled sector w/o "
5508                                                 "authentication\n"));
5509                                     *Status = STATUS_CSS_SCRAMBLED_SECTOR;
5510                                     break;
5511                                 case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
5512                                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5513                                                 "ClassInterpretSenseInfo: "
5514                                                 "Media region does not logical unit "
5515                                                 "region\n"));
5516                                     *Status = STATUS_CSS_REGION_MISMATCH;
5517                                     break;
5518                                 case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
5519                                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
5520                                                 "ClassInterpretSenseInfo: "
5521                                                 "Region set error -- region may "
5522                                                 "be permanent\n"));
5523                                     *Status = STATUS_CSS_RESETS_EXHAUSTED;
5524                                     break;
5525                             } // end switch of ASCQ for COPY_PROTECTION_FAILURE
5526 
5527                             break;
5528                         }
5529 
5530                         case SCSI_ADSENSE_MUSIC_AREA: {
5531                             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5532                                         "Music area\n"));
5533                             break;
5534                         }
5535 
5536                         case SCSI_ADSENSE_DATA_AREA: {
5537                             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5538                                         "Data area\n"));
5539                             break;
5540                         }
5541 
5542                         case SCSI_ADSENSE_VOLUME_OVERFLOW: {
5543                             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5544                                         "Volume overflow\n"));
5545                             break;
5546                         }
5547 
5548                     } // end switch (addlSenseCode)
5549 
5550                     break;
5551                 } // end SCSI_SENSE_ILLEGAL_REQUEST
5552 
5553                 case SCSI_SENSE_UNIT_ATTENTION: {
5554 
5555                     ULONG count;
5556 
5557                     //
5558                     // A media change may have occured so increment the change
5559                     // count for the physical device
5560                     //
5561 
5562                     count = InterlockedIncrement((volatile LONG *)&fdoExtension->MediaChangeCount);
5563                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,  "ClassInterpretSenseInfo: "
5564                                 "Media change count for device %d incremented to %#lx\n",
5565                                 fdoExtension->DeviceNumber, count));
5566 
5567 
5568                     switch (addlSenseCode) {
5569                         case SCSI_ADSENSE_MEDIUM_CHANGED: {
5570                             logRetryableError = FALSE;
5571                             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,  "ClassInterpretSenseInfo: "
5572                                         "Media changed\n"));
5573 
5574                             if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
5575                                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
5576                                             "Media Changed on non-removable device %p\n",
5577                                             Fdo));
5578                             }
5579                             ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
5580                             break;
5581                         }
5582 
5583                         case SCSI_ADSENSE_BUS_RESET: {
5584                             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5585                                         "Bus reset\n"));
5586                             break;
5587                         }
5588 
5589                         case SCSI_ADSENSE_PARAMETERS_CHANGED: {
5590                             logRetryableError = FALSE;
5591                             if (addlSenseCodeQual == SCSI_SENSEQ_CAPACITY_DATA_CHANGED) {
5592                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5593                                             "Device capacity changed (e.g. thinly provisioned LUN). Retry the request.\n"));
5594 
5595                                 ClassQueueCapacityChangedEventWorker(Fdo);
5596 
5597                                 //
5598                                 // Retry with 1 second delay as ClassQueueCapacityChangedEventWorker may trigger a couple of commands sent to disk.
5599                                 //
5600                                 retryInterval = 1;
5601                                 retry = TRUE;
5602                             }
5603                             break;
5604                         }
5605 
5606                         case SCSI_ADSENSE_LB_PROVISIONING: {
5607 
5608                             switch (addlSenseCodeQual) {
5609 
5610                                 case SCSI_SENSEQ_SOFT_THRESHOLD_REACHED: {
5611 
5612                                     logRetryableError = FALSE;
5613                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5614                                             "Device (%p) has hit a soft threshold.\n",
5615                                             Fdo));
5616 
5617                                     //
5618                                     // This indicates that a resource provisioned or thinly
5619                                     // provisioned device has hit a soft threshold.  Queue a
5620                                     // worker thread to log a system event and then retry the
5621                                     // original request.
5622                                     //
5623                                     ClassQueueThresholdEventWorker(Fdo);
5624                                     break;
5625                                 }
5626                                 default: {
5627                                     retry = FALSE;
5628                                     break;
5629                                 }
5630 
5631                             } // end  switch (addlSenseCodeQual)
5632                             break;
5633                         }
5634 
5635                         case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: {
5636 
5637                             if (addlSenseCodeQual == SCSI_SENSEQ_MICROCODE_CHANGED) {
5638                                 //
5639                                 // Device firmware has been changed. Retry the request.
5640                                 //
5641                                 logRetryableError = TRUE;
5642                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5643                                             "Device firmware has been changed.\n"));
5644 
5645                                 retryInterval = 1;
5646                                 retry = TRUE;
5647                             } else {
5648                                 //
5649                                 // Device information has changed, we need to rescan the
5650                                 // bus for changed information such as the capacity.
5651                                 //
5652                                 logRetryableError = FALSE;
5653                                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5654                                             "Device information changed. Invalidate the bus\n"));
5655 
5656                                 if (addlSenseCodeQual == SCSI_SENSEQ_INQUIRY_DATA_CHANGED) {
5657 
5658                                     ClassQueueProvisioningTypeChangedEventWorker(Fdo);
5659                                 }
5660 
5661                                 if (addlSenseCodeQual == SCSI_SENSEQ_INQUIRY_DATA_CHANGED ||
5662                                     addlSenseCodeQual == SCSI_SENSEQ_OPERATING_DEFINITION_CHANGED) {
5663 
5664                                     //
5665                                     // Since either the LB provisioning type changed, or the block/slab size
5666                                     // changed, next time anyone trying to query the FunctionSupportInfo, we
5667                                     // will requery the device.
5668                                     //
5669                                     InterlockedIncrement((volatile LONG *)&fdoExtension->FunctionSupportInfo->ChangeRequestCount);
5670                                 }
5671 
5672                                 IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
5673                                 retryInterval = 5;
5674                             }
5675                             break;
5676                         }
5677 
5678                         case SCSI_ADSENSE_OPERATOR_REQUEST: {
5679                             switch (addlSenseCodeQual) {
5680 
5681                                 case SCSI_SENSEQ_MEDIUM_REMOVAL: {
5682                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5683                                                 "Ejection request received!\n"));
5684                                     ClassSendEjectionNotification(fdoExtension);
5685                                     break;
5686                                 }
5687 
5688                                 case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
5689                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5690                                                 "Operator selected write permit?! "
5691                                                 "(unsupported!)\n"));
5692                                     break;
5693                                 }
5694 
5695                                 case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
5696                                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5697                                                 "Operator selected write protect?! "
5698                                                 "(unsupported!)\n"));
5699                                     break;
5700                                 }
5701                             }
5702                         }
5703 
5704                         case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
5705 
5706                             UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
5707 
5708                             *((PULONG)wmiEventData) = sizeof(UCHAR);
5709                             wmiEventData[sizeof(ULONG)] = addlSenseCodeQual;
5710 
5711                             //
5712                             // Don't log another eventlog if we have already logged once
5713                             // NOTE: this should have been interlocked, but the structure
5714                             //       was publicly defined to use a BOOLEAN (char).  Since
5715                             //       media only reports these errors once per X minutes,
5716                             //       the potential race condition is nearly non-existant.
5717                             //       the worst case is duplicate log entries, so ignore.
5718                             //
5719 
5720                             logError = FALSE;
5721                             if (fdoExtension->FailurePredicted == 0) {
5722                                 logError = TRUE;
5723                             }
5724                             fdoExtension->FailureReason = addlSenseCodeQual;
5725                             logStatus = IO_WRN_FAILURE_PREDICTED;
5726 
5727                             ClassNotifyFailurePredicted(fdoExtension,
5728                                                         (PUCHAR)wmiEventData,
5729                                                         sizeof(wmiEventData),
5730                                                         FALSE,   // do not log error
5731                                                         4,          // unique error value
5732                                                         SrbGetPathId(Srb),
5733                                                         SrbGetTargetId(Srb),
5734                                                         SrbGetLun(Srb));
5735 
5736                             fdoExtension->FailurePredicted = TRUE;
5737 
5738                             //
5739                             // Since this is a Unit Attention we need to make
5740                             // sure we retry this request.
5741                             //
5742                             retry = TRUE;
5743 
5744                             break;
5745                         }
5746 
5747                         default: {
5748                             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5749                                         "Unit attention\n"));
5750                             break;
5751                         }
5752 
5753                     } // end  switch (addlSenseCode)
5754 
5755                     if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
5756                     {
5757 
5758                         if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
5759                         {
5760                             //
5761                             // Set bit to indicate that media may have changed
5762                             // and volume needs verification.
5763                             //
5764 
5765                             SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
5766 
5767                             *Status = STATUS_VERIFY_REQUIRED;
5768                             retry = FALSE;
5769                         }
5770                         else {
5771                             *Status = STATUS_IO_DEVICE_ERROR;
5772                         }
5773                     }
5774                     else
5775                     {
5776                         *Status = STATUS_IO_DEVICE_ERROR;
5777                     }
5778 
5779                     break;
5780 
5781                 } // end SCSI_SENSE_UNIT_ATTENTION
5782 
5783                 case SCSI_SENSE_DATA_PROTECT: {
5784 
5785                     retry = FALSE;
5786 
5787                     if (addlSenseCode == SCSI_ADSENSE_WRITE_PROTECT)
5788                     {
5789                         switch (addlSenseCodeQual) {
5790 
5791                             case SCSI_SENSEQ_SPACE_ALLOC_FAILED_WRITE_PROTECT: {
5792 
5793                                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5794                                         "Device's (%p) resources are exhausted.\n",
5795                                         Fdo));
5796 
5797                                 ClassQueueResourceExhaustionEventWorker(Fdo);
5798 
5799                                 //
5800                                 // This indicates that a thinly-provisioned device has
5801                                 // hit a permanent resource exhaustion.  We need to
5802                                 // return this status code so that patmgr can take the
5803                                 // disk offline.
5804                                 //
5805                                 *Status = STATUS_DISK_RESOURCES_EXHAUSTED;
5806                                 break;
5807                             }
5808                             default:
5809                             {
5810                                 break;
5811                             }
5812 
5813                         } // end switch addlSenseCodeQual
5814                     }
5815                     else
5816                     {
5817                         if (IS_SCSIOP_WRITE(cdbOpcode)) {
5818                             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5819                                         "Media write protected\n"));
5820                             *Status = STATUS_MEDIA_WRITE_PROTECTED;
5821                         } else {
5822                             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
5823                                         "Access denied\n"));
5824                             *Status = STATUS_ACCESS_DENIED;
5825                         }
5826                     }
5827                     break;
5828                 } // end SCSI_SENSE_DATA_PROTECT
5829 
5830                 case SCSI_SENSE_BLANK_CHECK: {
5831                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5832                                 "Media blank check\n"));
5833                     retry = FALSE;
5834                     *Status = STATUS_NO_DATA_DETECTED;
5835                     break;
5836                 } // end SCSI_SENSE_BLANK_CHECK
5837 
5838                 case SCSI_SENSE_COPY_ABORTED: {
5839 
5840                     switch (addlSenseCode) {
5841 
5842                         case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
5843 
5844                             switch (addlSenseCodeQual) {
5845 
5846                                 //
5847                                 // 1. Target truncated the data transfer.
5848                                 //
5849                                 case SCSI_SENSEQ_DATA_UNDERRUN: {
5850 
5851                                     TracePrint((TRACE_LEVEL_WARNING,
5852                                                 TRACE_FLAG_IOCTL,
5853                                                 "ClassInterpretSenseInfo (%p): Data transfer was truncated (command %x)\n",
5854                                                 Fdo,
5855                                                 cdbOpcode));
5856 
5857                                     *Status = STATUS_SUCCESS;
5858                                     retry = FALSE;
5859                                     break;
5860                                 }
5861                             }
5862                             break;
5863                         }
5864                     }
5865                     break;
5866                 }
5867 
5868                 case SCSI_SENSE_ABORTED_COMMAND: {
5869                     if (ClasspIsOffloadDataTransferCommand(cdb)) {
5870 
5871                         switch (addlSenseCode) {
5872 
5873                             case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
5874 
5875                                 switch (addlSenseCodeQual) {
5876 
5877                                     //
5878                                     // 1. Target truncated the data transfer.
5879                                     //
5880                                     case SCSI_SENSEQ_DATA_UNDERRUN: {
5881 
5882                                         TracePrint((TRACE_LEVEL_WARNING,
5883                                                     TRACE_FLAG_IOCTL,
5884                                                     "ClassInterpretSenseInfo (%p): Target has truncated the data transfer (command %x)\n",
5885                                                     Fdo,
5886                                                     cdbOpcode));
5887 
5888                                         *Status = STATUS_SUCCESS;
5889                                         retry = FALSE;
5890                                         break;
5891                                     }
5892                                 }
5893                                 break;
5894                             }
5895 
5896                             case SCSI_ADSENSE_RESOURCE_FAILURE: {
5897 
5898                                 switch (addlSenseCodeQual) {
5899 
5900                                     //
5901                                     // 1. Copy manager wasn't able to finish the operation because of insuffient resources
5902                                     //    (e.g. microsnapshot failure on read, no space on write, etc.)
5903                                     //
5904                                     case SCSI_SENSEQ_INSUFFICIENT_RESOURCES: {
5905 
5906                                         TracePrint((TRACE_LEVEL_ERROR,
5907                                                     TRACE_FLAG_IOCTL,
5908                                                     "ClassInterpretSenseInfo (%p): Target has insufficient resources (command %x)\n",
5909                                                     Fdo,
5910                                                     cdb->CDB6GENERIC.OperationCode));
5911 
5912                                         *Status = STATUS_INSUFFICIENT_RESOURCES;
5913                                         retry = FALSE;
5914                                         break;
5915                                     }
5916                                 }
5917                                 break;
5918                             }
5919                         }
5920                     } else {
5921                         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5922                                     "Command aborted\n"));
5923                         *Status = STATUS_IO_DEVICE_ERROR;
5924                         retryInterval = 1;
5925                     }
5926                     break;
5927                 } // end SCSI_SENSE_ABORTED_COMMAND
5928 
5929                 default: {
5930                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5931                                 "Unrecognized sense code\n"));
5932                     *Status = STATUS_IO_DEVICE_ERROR;
5933                     break;
5934                 }
5935 
5936             } // end switch (senseKey)
5937 
5938 
5939 
5940             //
5941             // Try to determine bad sector information from sense data
5942             //
5943 
5944             if (((IS_SCSIOP_READWRITE(cdbOpcode))   ||
5945                 (cdbOpcode == SCSIOP_VERIFY)        ||
5946                 (cdbOpcode == SCSIOP_VERIFY16)) && cdb) {
5947 
5948                 if (isInformationValid)
5949                 {
5950                     readSector = 0;
5951                     badSector = information;
5952 
5953                     if (cdbOpcode == SCSIOP_READ16 || cdbOpcode == SCSIOP_WRITE16 || cdbOpcode == SCSIOP_VERIFY16) {
5954                         REVERSE_BYTES_QUAD(&readSector, &(cdb->AsByte[2]));
5955                     } else {
5956                         REVERSE_BYTES(&readSector, &(cdb->AsByte[2]));
5957                     }
5958 
5959                     if (cdbOpcode == SCSIOP_READ || cdbOpcode == SCSIOP_WRITE || cdbOpcode == SCSIOP_VERIFY) {
5960                         REVERSE_BYTES_SHORT(&index, &(cdb->CDB10.TransferBlocksMsb));
5961                     } else if (cdbOpcode == SCSIOP_READ6 ||  cdbOpcode == SCSIOP_WRITE6) {
5962                         index = cdb->CDB6READWRITE.TransferBlocks;
5963                     } else if(cdbOpcode == SCSIOP_READ12 || cdbOpcode == SCSIOP_WRITE12) {
5964                         REVERSE_BYTES(&index, &(cdb->CDB12.TransferLength));
5965                     } else {
5966                         REVERSE_BYTES(&index, &(cdb->CDB16.TransferLength));
5967                     }
5968 
5969                     //
5970                     // Make sure the bad sector is within the read sectors.
5971                     //
5972 
5973                     if (!(badSector >= readSector && badSector < readSector + index)) {
5974                         badSector = readSector;
5975                     }
5976                 }
5977             }
5978         }
5979 
5980 __ClassInterpretSenseInfo_ProcessingInvalidSenseBuffer:
5981 
5982         if (!validSense) {
5983 
5984             //
5985             // Request sense buffer not valid. No sense information
5986             // to pinpoint the error. Return general request fail.
5987             //
5988 
5989             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClassInterpretSenseInfo: "
5990                         "Request sense info not valid. SrbStatus %2x\n",
5991                         SRB_STATUS(Srb->SrbStatus)));
5992             retry = TRUE;
5993 
5994 
5995             switch (SRB_STATUS(Srb->SrbStatus)) {
5996                 case SRB_STATUS_ABORTED: {
5997 
5998                     //
5999                     // Update the error count for the device.
6000                     //
6001 
6002                     incrementErrorCount = TRUE;
6003                     *Status = STATUS_IO_TIMEOUT;
6004                     retryInterval = 1;
6005                     retry = TRUE;
6006                     break;
6007                 }
6008 
6009                 case SRB_STATUS_ERROR: {
6010 
6011                     *Status = STATUS_IO_DEVICE_ERROR;
6012                     if (SrbGetScsiStatus(Srb) == SCSISTAT_GOOD) {
6013 
6014                         //
6015                         // This is some strange return code.  Update the error
6016                         // count for the device.
6017                         //
6018 
6019                         incrementErrorCount = TRUE;
6020 
6021                     } else if (SrbGetScsiStatus(Srb) == SCSISTAT_BUSY) {
6022 
6023                         *Status = STATUS_DEVICE_NOT_READY;
6024                         logRetryableError = FALSE;
6025                     }
6026 
6027                     break;
6028                 }
6029 
6030                 case SRB_STATUS_INVALID_REQUEST: {
6031                     *Status = STATUS_INVALID_DEVICE_REQUEST;
6032                     retry = FALSE;
6033                     break;
6034                 }
6035 
6036                 case SRB_STATUS_INVALID_PATH_ID:
6037                 case SRB_STATUS_NO_DEVICE:
6038                 case SRB_STATUS_NO_HBA:
6039                 case SRB_STATUS_INVALID_LUN:
6040                 case SRB_STATUS_INVALID_TARGET_ID: {
6041                     *Status = STATUS_NO_SUCH_DEVICE;
6042                     retry = FALSE;
6043                     break;
6044                 }
6045 
6046                 case SRB_STATUS_SELECTION_TIMEOUT: {
6047                     logError = TRUE;
6048                     logStatus = IO_ERR_NOT_READY;
6049                     uniqueId = 260;
6050                     *Status = STATUS_DEVICE_NOT_CONNECTED;
6051                     retry = FALSE;
6052                     break;
6053                 }
6054 
6055                 case SRB_STATUS_TIMEOUT:
6056                 case SRB_STATUS_COMMAND_TIMEOUT: {
6057 
6058                     //
6059                     // Update the error count for the device.
6060                     //
6061                     incrementErrorCount = TRUE;
6062                     *Status = STATUS_IO_TIMEOUT;
6063                     break;
6064                 }
6065 
6066                 case SRB_STATUS_PARITY_ERROR:
6067                 case SRB_STATUS_UNEXPECTED_BUS_FREE:
6068 
6069                     //
6070                     // Update the error count for the device
6071                     // and fall through to below
6072                     //
6073                     incrementErrorCount = TRUE;
6074 
6075                 case SRB_STATUS_BUS_RESET: {
6076 
6077                     *Status = STATUS_IO_DEVICE_ERROR;
6078                     logRetryableError = FALSE;
6079                     break;
6080                 }
6081 
6082                 case SRB_STATUS_DATA_OVERRUN: {
6083 
6084                     *Status = STATUS_DATA_OVERRUN;
6085                     retry = FALSE;
6086 
6087                     //
6088                     // For some commands, we allocate a buffer that may be
6089                     // larger than necessary.  In these cases, the SRB may be
6090                     // returned with SRB_STATUS_DATA_OVERRUN to indicate a
6091                     // buffer *underrun*.  However, the command was still
6092                     // successful so we ensure STATUS_SUCCESS is returned.
6093                     // We will also prevent these errors from causing noise in
6094                     // the error logs.
6095                     //
6096                     if ((cdbOpcode == SCSIOP_MODE_SENSE && SrbGetDataTransferLength(Srb) <= cdb->MODE_SENSE.AllocationLength) ||
6097                         (cdbOpcode == SCSIOP_INQUIRY && SrbGetDataTransferLength(Srb) <= cdb->CDB6INQUIRY.AllocationLength)) {
6098                         *Status = STATUS_SUCCESS;
6099                         logErrorInternal = FALSE;
6100                         logError = FALSE;
6101                     } else if (cdbOpcode == SCSIOP_MODE_SENSE10) {
6102                         USHORT allocationLength;
6103                         REVERSE_BYTES_SHORT(&(cdb->MODE_SENSE10.AllocationLength), &allocationLength);
6104                         if (SrbGetDataTransferLength(Srb) <= allocationLength) {
6105                             *Status = STATUS_SUCCESS;
6106                             logErrorInternal = FALSE;
6107                             logError = FALSE;
6108                         }
6109                     } else if (ClasspIsReceiveTokenInformation(cdb)) {
6110                         ULONG allocationLength;
6111                         REVERSE_BYTES(&(cdb->RECEIVE_TOKEN_INFORMATION.AllocationLength), &allocationLength);
6112                         if (SrbGetDataTransferLength(Srb) <= allocationLength) {
6113                             *Status = STATUS_SUCCESS;
6114                             logErrorInternal = FALSE;
6115                             logError = FALSE;
6116                         }
6117                     }
6118 
6119                     break;
6120                 }
6121 
6122                 case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
6123 
6124                     //
6125                     // Update the error count for the device.
6126                     //
6127 
6128                     incrementErrorCount = TRUE;
6129                     *Status = STATUS_IO_DEVICE_ERROR;
6130 
6131                     //
6132                     // If there was  phase sequence error then limit the number of
6133                     // retries.
6134                     //
6135 
6136                     if (RetryCount > 1 ) {
6137                         retry = FALSE;
6138                     }
6139 
6140                     break;
6141                 }
6142 
6143                 case SRB_STATUS_REQUEST_FLUSHED: {
6144 
6145                     //
6146                     // If the status needs verification bit is set.  Then set
6147                     // the status to need verification and no retry; otherwise,
6148                     // just retry the request.
6149                     //
6150 
6151                     if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
6152 
6153                         *Status = STATUS_VERIFY_REQUIRED;
6154                         retry = FALSE;
6155 
6156                     } else {
6157                         *Status = STATUS_IO_DEVICE_ERROR;
6158                         logRetryableError = FALSE;
6159                     }
6160 
6161                     break;
6162                 }
6163 
6164 
6165                 default: {
6166                     logError = TRUE;
6167                     logStatus = IO_ERR_CONTROLLER_ERROR;
6168                     uniqueId = 259;
6169                     *Status = STATUS_IO_DEVICE_ERROR;
6170                     unhandledError = TRUE;
6171                     logRetryableError = FALSE;
6172                     break;
6173                 }
6174             }
6175 
6176 
6177             //
6178             // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
6179             // we know from a previous poll when the device will be ready (ETA)
6180             // we should delay the retry more appropriately than just guessing.
6181             //
6182             /*
6183             if (fdoExtension->MediaChangeDetectionInfo &&
6184                 fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
6185                 TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
6186                           NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
6187                 ) {
6188                 // check if Gesn.ReadyTime if greater than current tick count
6189                 // if so, delay that long (from 1 to 30 seconds max?)
6190                 // else, leave the guess of time alone.
6191             }
6192             */
6193 
6194         }
6195 
6196     }
6197 
6198     if (incrementErrorCount) {
6199 
6200         //
6201         // if any error count occurred, delay the retry of this io by
6202         // at least one second, if caller supports it.
6203         //
6204 
6205         if (retryInterval == 0) {
6206             retryInterval = 1;
6207         }
6208         ClasspPerfIncrementErrorCount(fdoExtension);
6209     }
6210 
6211     //
6212     // If there is a class specific error handler call it.
6213     //
6214 
6215     if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
6216 
6217         SCSI_REQUEST_BLOCK tempSrb = {0};
6218         PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Srb;
6219 
6220         //
6221         // If class driver does not support extended SRB and this is
6222         // an extended SRB, convert to legacy SRB and pass to class
6223         // driver.
6224         //
6225         if ((Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
6226             ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
6227               CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
6228             ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Srb);
6229             srbPtr = &tempSrb;
6230         }
6231 
6232         fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
6233                                                           srbPtr,
6234                                                           Status,
6235                                                           &retry);
6236     }
6237 
6238     //
6239     // If the caller wants to know the suggested retry interval tell them.
6240     //
6241 
6242     if (ARGUMENT_PRESENT(RetryInterval)) {
6243         *RetryInterval = retryInterval;
6244     }
6245 
6246     //
6247     // The RESERVE(6) / RELEASE(6) commands are optional. So
6248     // if they aren't supported, try the 10-byte equivalents
6249     //
6250 
6251     cdb = SrbGetCdb(Srb);
6252     if (cdb) {
6253         cdbOpcode = cdb->CDB6GENERIC.OperationCode;
6254     }
6255 
6256     if ((cdbOpcode == SCSIOP_RESERVE_UNIT ||
6257          cdbOpcode == SCSIOP_RELEASE_UNIT) && cdb)
6258     {
6259         if (*Status == STATUS_INVALID_DEVICE_REQUEST)
6260         {
6261             SrbSetCdbLength(Srb, 10);
6262             cdb->CDB10.OperationCode = (cdb->CDB6GENERIC.OperationCode == SCSIOP_RESERVE_UNIT) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
6263 
6264             SET_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6);
6265             retry = TRUE;
6266         }
6267     }
6268 
6269 #if DBG
6270 
6271     //
6272     // Ensure that for read/write requests, only return STATUS_DEVICE_BUSY if
6273     // reservation conflict.
6274     //
6275     if (IS_SCSIOP_READWRITE(cdbOpcode) && (*Status == STATUS_DEVICE_BUSY)) {
6276         NT_ASSERT(isReservationConflict == TRUE);
6277     }
6278 
6279 #endif
6280 
6281     /*
6282      *  LOG the error:
6283      *      If logErrorInternal is set, log the error in our internal log.
6284      *      If logError is set, also log the error in the system log.
6285      */
6286     if (logErrorInternal || logError) {
6287         ULONG totalSize;
6288         ULONG senseBufferSize = 0;
6289         IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
6290         CLASS_ERROR_LOG_DATA staticErrLogData = {0};
6291         SENSE_DATA convertedSenseBuffer = {0};
6292         UCHAR convertedSenseBufferLength = 0;
6293         BOOLEAN senseDataConverted = FALSE;
6294 
6295         //
6296         // Logic below assumes that IO_ERROR_LOG_PACKET + CLASS_ERROR_LOG_DATA
6297         // is less than ERROR_LOG_MAXIMUM_SIZE which is not true for extended SRB.
6298         // Given that classpnp currently does not use >16 byte CDB, we'll convert
6299         // an extended SRB to SCSI_REQUEST_BLOCK instead of changing this code.
6300         // More changes will need to be made when classpnp starts using >16 byte
6301         // CDBs.
6302         //
6303 
6304         //
6305         // Calculate the total size of the error log entry.
6306         // add to totalSize in the order that they are used.
6307         // the advantage to calculating all the sizes here is
6308         // that we don't have to do a bunch of extraneous checks
6309         // later on in this code path.
6310         //
6311         totalSize = sizeof(IO_ERROR_LOG_PACKET)  // required
6312                   + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
6313 
6314         //
6315         // also save any available extra sense data, up to the maximum errlog
6316         // packet size .  WMI should be used for real-time analysis.
6317         // the event log should only be used for post-mortem debugging.
6318         //
6319         if ((TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) && senseBuffer) {
6320 
6321             UCHAR validSenseBytes = 0;
6322             UCHAR senseInfoBufferLength = 0;
6323 
6324             senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
6325 
6326             //
6327             // If sense data is in Descriptor format, convert it to Fixed format
6328             // for the private log.
6329             //
6330 
6331             if (IsDescriptorSenseDataFormat(senseBuffer)) {
6332 
6333                 convertedSenseBufferLength = sizeof(convertedSenseBuffer);
6334 
6335                 senseDataConverted = ScsiConvertToFixedSenseFormat(senseBuffer,
6336                                                                    senseInfoBufferLength,
6337                                                                    (PVOID)&convertedSenseBuffer,
6338                                                                    convertedSenseBufferLength);
6339             }
6340 
6341             //
6342             // For System Log, copy the maximum amount of available sense data
6343             //
6344 
6345             if (ScsiGetTotalSenseByteCountIndicated(senseBuffer,
6346                                                     senseInfoBufferLength,
6347                                                     &validSenseBytes)) {
6348 
6349                 //
6350                 // If it is able to determine number of valid bytes,
6351                 // copy the maximum amount of available
6352                 // sense data that can be saved into the the errlog.
6353                 //
6354 
6355                 //
6356                 // set to save the most sense buffer possible
6357                 //
6358 
6359                 senseBufferSize = max(validSenseBytes, sizeof(staticErrLogData.SenseData));
6360                 senseBufferSize = min(senseBufferSize, senseInfoBufferLength);
6361 
6362             } else {
6363                 //
6364                 // it's smaller than required to read the total number of
6365                 // valid bytes, so just use the SenseInfoBufferLength field.
6366                 //
6367                 senseBufferSize = senseInfoBufferLength;
6368             }
6369 
6370             /*
6371              *  Bump totalSize by the number of extra senseBuffer bytes
6372              *  (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
6373              *  Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
6374              */
6375             if (senseBufferSize > sizeof(staticErrLogData.SenseData)){
6376                 totalSize += senseBufferSize-sizeof(staticErrLogData.SenseData);
6377                 if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
6378                     senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
6379                     totalSize = ERROR_LOG_MAXIMUM_SIZE;
6380                 }
6381             }
6382         }
6383 
6384         //
6385         // If we've used up all of our retry attempts, set the final status to
6386         // reflect the appropriate result.
6387         //
6388         // ISSUE: the test below should also check RetryCount to determine if we will actually retry,
6389         //            but there is no easy test because we'd have to consider the original retry count
6390         //            for the op; besides, InterpretTransferPacketError sometimes ignores the retry
6391         //            decision returned by this function.  So just ErrorRetried to be true in the majority case.
6392         //
6393         if (retry){
6394             staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
6395             staticErrLogData.ErrorRetried = TRUE;
6396         } else {
6397             staticErrLogEntry.FinalStatus = *Status;
6398         }
6399 
6400         //
6401         // Don't log generic IO_WARNING_PAGING_FAILURE message if either the
6402         // I/O is retried, or it completed successfully.
6403         //
6404         if (logStatus == IO_WARNING_PAGING_FAILURE &&
6405             (retry || NT_SUCCESS(*Status)) ) {
6406             logError = FALSE;
6407         }
6408 
6409         if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
6410             staticErrLogData.ErrorPaging = TRUE;
6411         }
6412         if (unhandledError) {
6413             staticErrLogData.ErrorUnhandled = TRUE;
6414         }
6415 
6416         //
6417         // Calculate the device offset if there is a geometry.
6418         //
6419         staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
6420         staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
6421         if (logStatus == -1){
6422             staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
6423         } else {
6424             staticErrLogEntry.ErrorCode = logStatus;
6425         }
6426 
6427         /*
6428          *  The dump data follows the IO_ERROR_LOG_PACKET
6429          */
6430         staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET);
6431 
6432         staticErrLogEntry.SequenceNumber = 0;
6433         staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
6434         staticErrLogEntry.IoControlCode = IoDeviceCode;
6435         staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
6436         staticErrLogEntry.UniqueErrorValue = uniqueId;
6437 
6438         KeQueryTickCount(&staticErrLogData.TickCount);
6439         staticErrLogData.PortNumber = (ULONG)-1;
6440 
6441         /*
6442          *  Save the entire contents of the SRB.
6443          */
6444         if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
6445             ClasspConvertToScsiRequestBlock(&staticErrLogData.Srb, (PSTORAGE_REQUEST_BLOCK)Srb);
6446         } else {
6447             staticErrLogData.Srb = *(PSCSI_REQUEST_BLOCK)Srb;
6448         }
6449 
6450         /*
6451          *  For our private log, save just the default length of the SENSE_DATA.
6452          */
6453 
6454         if ((senseBufferSize != 0) && senseBuffer) {
6455 
6456             //
6457             // If sense buffer is in Fixed format, put it in the private log
6458             //
6459             // If sense buffer is in Descriptor format, put it in the private log if conversion to Fixed format
6460             // succeeded. Otherwise, do not put it in the private log.
6461             //
6462             // If sense buffer is in unknown format, the device or the driver probably does not populate
6463             // the first byte of sense data, we probably still want to log error in this case assuming
6464             // it's fixed format, so that its sense key, its additional sense code, and its additional sense code
6465             // qualifier would be shown in the debugger extension output. By doing so, it minimizes any potential
6466             // negative impacts to our ability to diagnose issue.
6467             //
6468             if (IsDescriptorSenseDataFormat(senseBuffer)) {
6469                 if (senseDataConverted) {
6470                     RtlCopyMemory(&staticErrLogData.SenseData, &convertedSenseBuffer, min(convertedSenseBufferLength, sizeof(staticErrLogData.SenseData)));
6471                 }
6472             } else {
6473                 RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(staticErrLogData.SenseData)));
6474             }
6475         }
6476 
6477         /*
6478          *  Save the error log in our context.
6479          *  We only save the default sense buffer length.
6480          */
6481         if (logErrorInternal) {
6482             KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
6483             fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
6484             fdoData->ErrorLogNextIndex++;
6485             fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
6486             KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
6487         }
6488 
6489         /*
6490          *  Log an event if an IO is being retried for reasons that may indicate
6491          *  a transient/permanent problem with the I_T_L nexus. But log the event
6492          *  only once per retried IO.
6493          */
6494         if (!IS_SCSIOP_READWRITE(cdbOpcode) ||
6495             !retry ||
6496             (RetryCount != 0)) {
6497 
6498             logRetryableError = FALSE;
6499         }
6500 
6501         if (logRetryableError) {
6502 
6503             logError = TRUE;
6504         }
6505 
6506         /*
6507          *  If logError is set, also save this log in the system's error log.
6508          *  But make sure we don't log TUR failures over and over
6509          *  (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
6510          */
6511 
6512         if (logError)
6513         {
6514             //
6515             // We do not want to log certain system events repetitively
6516             //
6517 
6518             cdb = SrbGetCdb(Srb);
6519             if (cdb) {
6520                 switch (cdb->CDB10.OperationCode)
6521                 {
6522                     case SCSIOP_TEST_UNIT_READY:
6523                     {
6524                         if (fdoData->LoggedTURFailureSinceLastIO)
6525                         {
6526                             logError = FALSE;
6527                         }
6528                         else
6529                         {
6530                             fdoData->LoggedTURFailureSinceLastIO = TRUE;
6531                         }
6532 
6533                         break;
6534                     }
6535 
6536                     case SCSIOP_SYNCHRONIZE_CACHE:
6537                     {
6538                         if (fdoData->LoggedSYNCFailure)
6539                         {
6540                             logError = FALSE;
6541                         }
6542                         else
6543                         {
6544                             fdoData->LoggedSYNCFailure = TRUE;
6545                         }
6546 
6547                         break;
6548                     }
6549                 }
6550             }
6551         }
6552 
6553         if (logError){
6554 
6555             if (logRetryableError) {
6556 
6557                 NT_ASSERT(IS_SCSIOP_READWRITE(cdbOpcode));
6558 
6559                 //
6560                 // A large Disk TimeOutValue (like 60 seconds) results in giving a command a
6561                 // large window to complete in. However, if the target returns a retryable error
6562                 // just prior to the command timing out, and if multiple retries kick in, it may
6563                 // take a significantly long time for the request to complete back to the
6564                 // application, leading to a user perception of a hung system. So log an event
6565                 // for retried IO so that an admin can help explain the reason for this behavior.
6566                 //
6567                 ClasspQueueLogIOEventWithContextWorker(Fdo,
6568                                                        senseBufferSize,
6569                                                        senseBuffer,
6570                                                        SRB_STATUS(Srb->SrbStatus),
6571                                                        SrbGetScsiStatus(Srb),
6572                                                        (ULONG)IO_WARNING_IO_OPERATION_RETRIED,
6573                                                        cdbLength,
6574                                                        cdb,
6575                                                        NULL);
6576 
6577             } else {
6578 
6579                 PIO_ERROR_LOG_PACKET errorLogEntry;
6580                 PCLASS_ERROR_LOG_DATA errlogData;
6581 
6582                 errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
6583                 if (errorLogEntry){
6584                     errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
6585 
6586                     *errorLogEntry = staticErrLogEntry;
6587                     *errlogData = staticErrLogData;
6588 
6589                     /*
6590                      *  For the system log, copy as much of the sense buffer as possible.
6591                      */
6592                     if ((senseBufferSize != 0) && senseBuffer) {
6593                         RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
6594                     }
6595 
6596                     /*
6597                      *  Write the error log packet to the system error logging thread.
6598                      *  It will be freed by the kernel.
6599                      */
6600                     IoWriteErrorLogEntry(errorLogEntry);
6601                 }
6602             }
6603         }
6604     }
6605 
6606     return retry;
6607 
6608 } // end ClassInterpretSenseInfo()
6609 
6610 
6611 /*++////////////////////////////////////////////////////////////////////////////
6612 
6613 ClassModeSense()
6614 
6615 Routine Description:
6616 
6617     This routine sends a mode sense command to a target ID and returns
6618     when it is complete.
6619 
6620 Arguments:
6621 
6622     Fdo - Supplies the functional device object associated with this request.
6623 
6624     ModeSenseBuffer - Supplies a buffer to store the sense data.
6625 
6626     Length - Supplies the length in bytes of the mode sense buffer.
6627 
6628     PageMode - Supplies the page or pages of mode sense data to be retrived.
6629 
6630 Return Value:
6631 
6632     Length of the transferred data is returned.
6633 
6634 --*/
6635 ULONG
6636 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
6637 ClassModeSense(
6638     _In_ PDEVICE_OBJECT Fdo,
6639     _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
6640     _In_ ULONG Length,
6641     _In_ UCHAR PageMode
6642     )
6643 {
6644     PAGED_CODE();
6645 
6646     return ClasspModeSense(Fdo,
6647                            ModeSenseBuffer,
6648                            Length,
6649                            PageMode,
6650                            MODE_SENSE_CURRENT_VALUES);
6651 }
6652 
6653 
6654 ULONG
6655 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
6656 ClassModeSenseEx(
6657     _In_ PDEVICE_OBJECT Fdo,
6658     _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
6659     _In_ ULONG Length,
6660     _In_ UCHAR PageMode,
6661     _In_ UCHAR PageControl
6662     )
6663 {
6664     PAGED_CODE();
6665 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
6666     return ClasspModeSense(Fdo,
6667                            ModeSenseBuffer,
6668                            Length,
6669                            PageMode,
6670                            PageControl);
6671 #else
6672     UNREFERENCED_PARAMETER(Fdo);
6673     UNREFERENCED_PARAMETER(ModeSenseBuffer);
6674     UNREFERENCED_PARAMETER(Length);
6675     UNREFERENCED_PARAMETER(PageMode);
6676     UNREFERENCED_PARAMETER(PageControl);
6677     return 0;
6678 #endif
6679 }
6680 
6681 ULONG ClasspModeSense(
6682     _In_ PDEVICE_OBJECT Fdo,
6683     _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
6684     _In_ ULONG Length,
6685     _In_ UCHAR PageMode,
6686     _In_ UCHAR PageControl
6687     )
6688 /*
6689 Routine Description:
6690 
6691     This routine sends a mode sense command to a target ID and returns
6692     when it is complete.
6693 
6694 Arguments:
6695 
6696     Fdo - Supplies the functional device object associated with this request.
6697 
6698     ModeSenseBuffer - Supplies a buffer to store the sense data.
6699 
6700     Length - Supplies the length in bytes of the mode sense buffer.
6701 
6702     PageMode - Supplies the page or pages of mode sense data to be retrived.
6703 
6704     PageControl - Supplies the page control value of the request, which is
6705         one of the following:
6706         MODE_SENSE_CURRENT_VALUES
6707         MODE_SENSE_CHANGEABLE_VALUES
6708         MODE_SENSE_DEFAULT_VAULES
6709         MODE_SENSE_SAVED_VALUES
6710 
6711 Return Value:
6712 
6713     Length of the transferred data is returned.
6714 
6715 --*/
6716 {
6717     ULONG lengthTransferred = 0;
6718     PMDL senseBufferMdl;
6719 
6720     PAGED_CODE();
6721 
6722     senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
6723     if (senseBufferMdl){
6724 
6725         TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
6726         if (pkt){
6727             KEVENT event;
6728             IRP pseudoIrp = {0};
6729 
6730             /*
6731              *  Store the number of packets servicing the irp (one)
6732              *  inside the original IRP.  It will be used to counted down
6733              *  to zero when the packet completes.
6734              *  Initialize the original IRP's status to success.
6735              *  If the packet fails, we will set it to the error status.
6736              */
6737             pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
6738             pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
6739             pseudoIrp.IoStatus.Information = 0;
6740             pseudoIrp.MdlAddress = senseBufferMdl;
6741 
6742             /*
6743              *  Set this up as a SYNCHRONOUS transfer, submit it,
6744              *  and wait for the packet to complete.  The result
6745              *  status will be written to the original irp.
6746              */
6747             NT_ASSERT(Length <= 0x0ff);
6748             KeInitializeEvent(&event, SynchronizationEvent, FALSE);
6749             SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, 0, &pseudoIrp, PageControl);
6750             SubmitTransferPacket(pkt);
6751             (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
6752 
6753             if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
6754                 lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
6755             }
6756             else {
6757                 /*
6758                  *  This request can sometimes fail legitimately
6759                  *  (e.g. when a SCSI device is attached but turned off)
6760                  *  so this is not necessarily a device/driver bug.
6761                  */
6762                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClasspModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
6763             }
6764         }
6765 
6766         FreeDeviceInputMdl(senseBufferMdl);
6767     }
6768 
6769     return lengthTransferred;
6770 }
6771 
6772 /*++////////////////////////////////////////////////////////////////////////////
6773 
6774 ClassFindModePage()
6775 
6776 Routine Description:
6777 
6778     This routine scans through the mode sense data and finds the requested
6779     mode sense page code.
6780 
6781 Arguments:
6782     ModeSenseBuffer - Supplies a pointer to the mode sense data.
6783 
6784     Length - Indicates the length of valid data.
6785 
6786     PageMode - Supplies the page mode to be searched for.
6787 
6788     Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
6789 
6790 Return Value:
6791 
6792     A pointer to the the requested mode page.  If the mode page was not found
6793     then NULL is return.
6794 
6795 --*/
6796 PVOID
6797 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
6798 ClassFindModePage(
6799     _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
6800     _In_ ULONG Length,
6801     _In_ UCHAR PageMode,
6802     _In_ BOOLEAN Use6Byte
6803     )
6804 {
6805     PUCHAR limit;
6806     ULONG  parameterHeaderLength;
6807     PVOID result = NULL;
6808 
6809     limit = (PUCHAR)ModeSenseBuffer + Length;
6810     parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
6811 
6812     if (Length >= parameterHeaderLength) {
6813 
6814         PMODE_PARAMETER_HEADER10 modeParam10;
6815         ULONG blockDescriptorLength;
6816 
6817         /*
6818          *  Skip the mode select header and block descriptors.
6819          */
6820         if (Use6Byte){
6821             blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
6822         }
6823         else {
6824             modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
6825             blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
6826         }
6827 
6828         ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
6829 
6830         //
6831         // ModeSenseBuffer now points at pages.  Walk the pages looking for the
6832         // requested page until the limit is reached.
6833         //
6834 
6835         while (ModeSenseBuffer +
6836                RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < (PCHAR)limit) {
6837 
6838             if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
6839 
6840                 /*
6841                  * found the mode page.  make sure it's safe to touch it all
6842                  * before returning the pointer to caller
6843                  */
6844 
6845                 if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > (PCHAR)limit) {
6846                     /*
6847                      *  Return NULL since the page is not safe to access in full
6848                      */
6849                     result = NULL;
6850                 }
6851                 else {
6852                     result = ModeSenseBuffer;
6853                 }
6854                 break;
6855             }
6856 
6857             //
6858             // Advance to the next page which is 4-byte-aligned offset after this page.
6859             //
6860             ModeSenseBuffer +=
6861                 ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
6862                 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
6863 
6864         }
6865     }
6866 
6867     return result;
6868 } // end ClassFindModePage()
6869 
6870 
6871 NTSTATUS
6872 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
6873 ClassModeSelect(
6874     _In_ PDEVICE_OBJECT Fdo,
6875     _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
6876     _In_ ULONG Length,
6877     _In_ BOOLEAN SavePages
6878     )
6879 {
6880 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
6881     return ClasspModeSelect(Fdo,
6882                             ModeSelectBuffer,
6883                             Length,
6884                             SavePages);
6885 #else
6886     UNREFERENCED_PARAMETER(Fdo);
6887     UNREFERENCED_PARAMETER(ModeSelectBuffer);
6888     UNREFERENCED_PARAMETER(Length);
6889     UNREFERENCED_PARAMETER(SavePages);
6890     return STATUS_NOT_SUPPORTED;
6891 #endif
6892 }
6893 
6894 /*++
6895 ClasspModeSelect()
6896 
6897 Routine Description:
6898 
6899     This routine sends a mode select command to a target ID and returns
6900     when it is complete.
6901 
6902 Arguments:
6903 
6904     Fdo - Supplies the functional device object associated with this request.
6905 
6906     ModeSelectBuffer - Supplies a buffer to the select data.
6907 
6908     Length - Supplies the length in bytes of the mode select buffer.
6909 
6910     SavePages - Specifies the value of the save pages (SP) bit in the mode
6911         select command.
6912 
6913 Return Value:
6914 
6915     NTSTATUS code of the request.
6916 
6917 --*/
6918 NTSTATUS
6919 ClasspModeSelect(
6920     _In_ PDEVICE_OBJECT Fdo,
6921     _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
6922     _In_ ULONG Length,
6923     _In_ BOOLEAN SavePages
6924     )
6925 {
6926 
6927     PMDL senseBufferMdl;
6928     NTSTATUS status = STATUS_UNSUCCESSFUL;
6929 
6930     senseBufferMdl = BuildDeviceInputMdl(ModeSelectBuffer, Length);
6931     if (senseBufferMdl) {
6932 
6933         TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
6934         if (pkt){
6935             KEVENT event;
6936             IRP pseudoIrp = {0};
6937 
6938             /*
6939              *  Store the number of packets servicing the irp (one)
6940              *  inside the original IRP.  It will be used to counted down
6941              *  to zero when the packet completes.
6942              *  Initialize the original IRP's status to success.
6943              *  If the packet fails, we will set it to the error status.
6944              */
6945             pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
6946             pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
6947             pseudoIrp.IoStatus.Information = 0;
6948             pseudoIrp.MdlAddress = senseBufferMdl;
6949 
6950             /*
6951              *  Set this up as a SYNCHRONOUS transfer, submit it,
6952              *  and wait for the packet to complete.  The result
6953              *  status will be written to the original irp.
6954              */
6955             NT_ASSERT(Length <= 0x0ff);
6956             KeInitializeEvent(&event, SynchronizationEvent, FALSE);
6957             SetupModeSelectTransferPacket(pkt, &event, ModeSelectBuffer, (UCHAR)Length, SavePages, &pseudoIrp);
6958             SubmitTransferPacket(pkt);
6959             (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
6960 
6961             if (!NT_SUCCESS(pseudoIrp.IoStatus.Status)){
6962                 /*
6963                  *  This request can sometimes fail legitimately
6964                  *  (e.g. when a SCSI device is attached but turned off)
6965                  *  so this is not necessarily a device/driver bug.
6966                  */
6967                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassModeSelect on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
6968             }
6969 
6970             status = pseudoIrp.IoStatus.Status;
6971         }
6972 
6973         FreeDeviceInputMdl(senseBufferMdl);
6974     } else {
6975         status = STATUS_INSUFFICIENT_RESOURCES;
6976     }
6977 
6978     return status;
6979 }
6980 
6981 /*++////////////////////////////////////////////////////////////////////////////
6982 
6983 ClassSendSrbAsynchronous()
6984 
6985 Routine Description:
6986 
6987     This routine takes a partially built Srb and an Irp and sends it down to
6988     the port driver.
6989 
6990     This routine must be called with the remove lock held for the specified
6991     Irp.
6992 
6993 Arguments:
6994 
6995     Fdo - Supplies the functional device object for the orginal request.
6996 
6997     Srb - Supplies a paritally build ScsiRequestBlock.  In particular, the
6998         CDB and the SRB timeout value must be filled in.  The SRB must not be
6999         allocated from zone.
7000 
7001     Irp - Supplies the requesting Irp.
7002 
7003     BufferAddress - Supplies a pointer to the buffer to be transfered.
7004 
7005     BufferLength - Supplies the length of data transfer.
7006 
7007     WriteToDevice - Indicates the data transfer will be from system memory to
7008         device.
7009 
7010 Return Value:
7011 
7012     Returns STATUS_PENDING if the request is dispatched (since the
7013     completion routine may change the irp's status value we cannot simply
7014     return the value of the dispatch)
7015 
7016     or returns a status value to indicate why it failed.
7017 
7018 --*/
7019 _Success_(return == STATUS_PENDING)
7020 NTSTATUS
7021 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
7022 ClassSendSrbAsynchronous(
7023     _In_ PDEVICE_OBJECT Fdo,
7024     _Inout_ __on_failure(__drv_freesMem(Mem)) __drv_aliasesMem PSCSI_REQUEST_BLOCK _Srb,
7025     _In_ PIRP Irp,
7026     _In_reads_bytes_opt_(BufferLength) __drv_aliasesMem PVOID BufferAddress,
7027     _In_ ULONG BufferLength,
7028     _In_ BOOLEAN WriteToDevice
7029     )
7030 {
7031 
7032     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
7033     PIO_STACK_LOCATION irpStack;
7034     PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
7035 
7036     ULONG savedFlags;
7037 
7038     if (Srb->Function != SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
7039         //
7040         // Write length to SRB.
7041         //
7042 
7043         Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
7044 
7045         //
7046         // Set SCSI bus address.
7047         //
7048 
7049         Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
7050     }
7051 
7052     //
7053     // This is a violation of the SCSI spec but it is required for
7054     // some targets.
7055     //
7056 
7057     // Srb->Cdb[1] |= deviceExtension->Lun << 5;
7058 
7059     //
7060     // Indicate auto request sense by specifying buffer and size.
7061     //
7062 
7063     SrbSetSenseInfoBuffer(Srb, fdoExtension->SenseData);
7064     SrbSetSenseInfoBufferLength(Srb, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(fdoExtension));
7065 
7066     SrbSetDataBuffer(Srb, BufferAddress);
7067 
7068     //
7069     // Set the transfer length.
7070     //
7071     SrbSetDataTransferLength(Srb, BufferLength);
7072 
7073     //
7074     // Save the class driver specific flags away.
7075     //
7076 
7077     savedFlags = SrbGetSrbFlags(Srb) & SRB_FLAGS_CLASS_DRIVER_RESERVED;
7078 
7079     //
7080     // Allow the caller to specify that they do not wish
7081     // IoStartNextPacket() to be called in the completion routine.
7082     //
7083 
7084     SET_FLAG(savedFlags, (SrbGetSrbFlags(Srb) & SRB_FLAGS_DONT_START_NEXT_PACKET));
7085 
7086     //
7087     // If caller wants to this request to be tagged, save this fact.
7088     //
7089 
7090     if ( TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_QUEUE_ACTION_ENABLE) &&
7091          ( SRB_SIMPLE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ||
7092            SRB_HEAD_OF_QUEUE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ||
7093            SRB_ORDERED_QUEUE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ) ) {
7094 
7095         SET_FLAG(savedFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
7096         if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_NO_QUEUE_FREEZE)) {
7097             SET_FLAG(savedFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
7098         }
7099     }
7100 
7101     if (BufferAddress != NULL) {
7102 
7103         //
7104         // Build Mdl if necessary.
7105         //
7106 
7107         if (Irp->MdlAddress == NULL) {
7108 
7109             PMDL mdl;
7110 
7111             mdl = IoAllocateMdl(BufferAddress,
7112                                 BufferLength,
7113                                 FALSE,
7114                                 FALSE,
7115                                 Irp);
7116 
7117             if ((mdl == NULL) || (Irp->MdlAddress == NULL)) {
7118 
7119                 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
7120 
7121                 //
7122                 // ClassIoComplete() would have free'd the srb
7123                 //
7124 
7125                 if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
7126                     FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
7127                 }
7128                 ClassFreeOrReuseSrb(fdoExtension, (PSCSI_REQUEST_BLOCK)Srb);
7129                 ClassReleaseRemoveLock(Fdo, Irp);
7130                 ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
7131 
7132                 return STATUS_INSUFFICIENT_RESOURCES;
7133             }
7134 
7135             SET_FLAG(savedFlags, SRB_CLASS_FLAGS_FREE_MDL);
7136 
7137             MmBuildMdlForNonPagedPool(Irp->MdlAddress);
7138 
7139         } else {
7140 
7141             //
7142             // Make sure the buffer requested matches the MDL.
7143             //
7144 
7145             NT_ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
7146         }
7147 
7148         //
7149         // Set read flag.
7150         //
7151 
7152         SrbAssignSrbFlags(Srb, WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN);
7153 
7154     } else {
7155 
7156         //
7157         // Clear flags.
7158         //
7159 
7160         SrbAssignSrbFlags(Srb, SRB_FLAGS_NO_DATA_TRANSFER);
7161     }
7162 
7163     //
7164     // Restore saved flags.
7165     //
7166 
7167     SrbSetSrbFlags(Srb, savedFlags);
7168 
7169     //
7170     // Disable synchronous transfer for these requests.
7171     //
7172 
7173     SrbSetSrbFlags(Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
7174 
7175     //
7176     // Zero out status.
7177     //
7178 
7179     SrbSetScsiStatus(Srb, 0);
7180     Srb->SrbStatus = 0;
7181 
7182     SrbSetNextSrb(Srb, NULL);
7183 
7184     //
7185     // Save a few parameters in the current stack location.
7186     //
7187 
7188     irpStack = IoGetCurrentIrpStackLocation(Irp);
7189 
7190     //
7191     // Save retry count in current Irp stack.
7192     //
7193 
7194     irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
7195 
7196     //
7197     // Set up IoCompletion routine address.
7198     //
7199 
7200     IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
7201 
7202     //
7203     // Get next stack location and
7204     // set major function code.
7205     //
7206 
7207     irpStack = IoGetNextIrpStackLocation(Irp);
7208 
7209     irpStack->MajorFunction = IRP_MJ_SCSI;
7210 
7211     //
7212     // Save SRB address in next stack for port driver.
7213     //
7214 
7215     irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;
7216 
7217     //
7218     // Set up Irp Address.
7219     //
7220 
7221     SrbSetOriginalRequest(Srb, Irp);
7222 
7223     //
7224     // Call the port driver to process the request.
7225     //
7226 
7227     IoMarkIrpPending(Irp);
7228 
7229     IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
7230 
7231     return STATUS_PENDING;
7232 
7233 } // end ClassSendSrbAsynchronous()
7234 
7235 /*++////////////////////////////////////////////////////////////////////////////
7236 
7237 ClassDeviceControlDispatch()
7238 
7239 Routine Description:
7240 
7241     The routine is the common class driver device control dispatch entry point.
7242     This routine is invokes the device-specific drivers DeviceControl routine,
7243     (which may call the Class driver's common DeviceControl routine).
7244 
7245 Arguments:
7246 
7247     DeviceObject - Supplies a pointer to the device object for this request.
7248 
7249     Irp - Supplies the Irp making the request.
7250 
7251 Return Value:
7252 
7253    Returns the status returned from the device-specific driver.
7254 
7255 --*/
7256 NTSTATUS
7257 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
7258 ClassDeviceControlDispatch(
7259     PDEVICE_OBJECT DeviceObject,
7260     PIRP Irp
7261     )
7262 {
7263 
7264     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7265     ULONG isRemoved;
7266 
7267     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
7268     _Analysis_assume_(isRemoved);
7269     if(isRemoved) {
7270 
7271         ClassReleaseRemoveLock(DeviceObject, Irp);
7272 
7273         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
7274         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7275         return STATUS_DEVICE_DOES_NOT_EXIST;
7276     }
7277 
7278     //
7279     // Call the class specific driver DeviceControl routine.
7280     // If it doesn't handle it, it will call back into ClassDeviceControl.
7281     //
7282 
7283     NT_ASSERT(commonExtension->DevInfo->ClassDeviceControl);
7284 
7285     return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
7286 } // end ClassDeviceControlDispatch()
7287 
7288 
7289 /*++////////////////////////////////////////////////////////////////////////////
7290 
7291 ClassDeviceControl()
7292 
7293 Routine Description:
7294 
7295     The routine is the common class driver device control dispatch function.
7296     This routine is called by a class driver when it get an unrecognized
7297     device control request.  This routine will perform the correct action for
7298     common requests such as lock media.  If the device request is unknown it
7299     passed down to the next level.
7300 
7301     This routine must be called with the remove lock held for the specified
7302     irp.
7303 
7304 Arguments:
7305 
7306     DeviceObject - Supplies a pointer to the device object for this request.
7307 
7308     Irp - Supplies the Irp making the request.
7309 
7310 Return Value:
7311 
7312    Returns back a STATUS_PENDING or a completion status.
7313 
7314 --*/
7315 NTSTATUS
7316 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
7317 ClassDeviceControl(
7318     _In_ PDEVICE_OBJECT DeviceObject,
7319     _Inout_ PIRP Irp
7320     )
7321 {
7322     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
7323 
7324     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
7325     PIO_STACK_LOCATION nextStack = NULL;
7326 
7327     ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
7328 
7329     PSCSI_REQUEST_BLOCK srb = NULL;
7330     PCDB cdb = NULL;
7331 
7332     NTSTATUS status;
7333     ULONG modifiedIoControlCode = 0;
7334     GUID activityId = {0};
7335 
7336 
7337     //
7338     // If this is a pass through I/O control, set the minor function code
7339     // and device address and pass it to the port driver.
7340     //
7341 
7342     if ( (controlCode == IOCTL_SCSI_PASS_THROUGH) ||
7343          (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ||
7344          (controlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
7345          (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX) ) {
7346 
7347 
7348 
7349         //
7350         // Validiate the user buffer for SCSI pass through.
7351         // For pass through EX: as the handler will validate the size anyway,
7352         //                      do not apply the similar check and leave the work to the handler.
7353         //
7354         if ( (controlCode == IOCTL_SCSI_PASS_THROUGH) ||
7355              (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ) {
7356 
7357         #if BUILD_WOW64_ENABLED && defined(_WIN64)
7358 
7359             if (IoIs32bitProcess(Irp)) {
7360 
7361                 if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
7362 
7363                     Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
7364 
7365                     ClassReleaseRemoveLock(DeviceObject, Irp);
7366                     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7367 
7368                     status = STATUS_INVALID_PARAMETER;
7369                     goto SetStatusAndReturn;
7370                 }
7371             }
7372             else
7373 
7374         #endif
7375 
7376             {
7377                 if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
7378                     sizeof(SCSI_PASS_THROUGH)) {
7379 
7380                     Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
7381 
7382                     ClassReleaseRemoveLock(DeviceObject, Irp);
7383                     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7384 
7385                     status = STATUS_INVALID_PARAMETER;
7386                     goto SetStatusAndReturn;
7387                 }
7388             }
7389         }
7390 
7391 
7392         IoCopyCurrentIrpStackLocationToNext(Irp);
7393 
7394         nextStack = IoGetNextIrpStackLocation(Irp);
7395         nextStack->MinorFunction = 1;
7396 
7397         ClassReleaseRemoveLock(DeviceObject, Irp);
7398 
7399         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
7400         goto SetStatusAndReturn;
7401 
7402     }
7403 
7404     Irp->IoStatus.Information = 0;
7405 
7406 
7407     switch (controlCode) {
7408 
7409         case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
7410 
7411             PMOUNTDEV_UNIQUE_ID uniqueId;
7412 
7413             if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
7414                 status = STATUS_INVALID_PARAMETER;
7415                 break;
7416             }
7417 
7418             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7419                 sizeof(MOUNTDEV_UNIQUE_ID)) {
7420 
7421                 status = STATUS_BUFFER_TOO_SMALL;
7422                 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
7423                 break;
7424             }
7425 
7426             uniqueId = Irp->AssociatedIrp.SystemBuffer;
7427             RtlZeroMemory(uniqueId, sizeof(MOUNTDEV_UNIQUE_ID));
7428             uniqueId->UniqueIdLength =
7429                     commonExtension->MountedDeviceInterfaceName.Length;
7430 
7431             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7432                 sizeof(USHORT) + uniqueId->UniqueIdLength) {
7433 
7434                 status = STATUS_BUFFER_OVERFLOW;
7435                 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
7436                 break;
7437             }
7438 
7439             RtlCopyMemory(uniqueId->UniqueId,
7440                           commonExtension->MountedDeviceInterfaceName.Buffer,
7441                           uniqueId->UniqueIdLength);
7442 
7443             status = STATUS_SUCCESS;
7444             Irp->IoStatus.Information = sizeof(USHORT) +
7445                                         uniqueId->UniqueIdLength;
7446             break;
7447         }
7448 
7449         case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
7450 
7451             PMOUNTDEV_NAME name;
7452 
7453             NT_ASSERT(commonExtension->DeviceName.Buffer);
7454 
7455             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7456                 sizeof(MOUNTDEV_NAME)) {
7457 
7458                 status = STATUS_BUFFER_TOO_SMALL;
7459                 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
7460                 break;
7461             }
7462 
7463             name = Irp->AssociatedIrp.SystemBuffer;
7464             RtlZeroMemory(name, sizeof(MOUNTDEV_NAME));
7465             name->NameLength = commonExtension->DeviceName.Length;
7466 
7467             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7468                 sizeof(USHORT) + name->NameLength) {
7469 
7470                 status = STATUS_BUFFER_OVERFLOW;
7471                 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
7472                 break;
7473             }
7474 
7475             RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
7476                           name->NameLength);
7477 
7478             status = STATUS_SUCCESS;
7479             Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
7480             break;
7481         }
7482 
7483         case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
7484 
7485             PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
7486             WCHAR driveLetterNameBuffer[10] = {0};
7487             RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
7488             PWSTR valueName;
7489             UNICODE_STRING driveLetterName;
7490 
7491             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7492                 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
7493 
7494                 status = STATUS_BUFFER_TOO_SMALL;
7495                 Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
7496                 break;
7497             }
7498 
7499             valueName = ExAllocatePoolWithTag(
7500                             PagedPool,
7501                             commonExtension->DeviceName.Length + sizeof(WCHAR),
7502                             '8CcS');
7503 
7504             if (!valueName) {
7505                 status = STATUS_INSUFFICIENT_RESOURCES;
7506                 break;
7507             }
7508 
7509             RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
7510                           commonExtension->DeviceName.Length);
7511             valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
7512 
7513             driveLetterName.Buffer = driveLetterNameBuffer;
7514             driveLetterName.MaximumLength = sizeof(driveLetterNameBuffer);
7515             driveLetterName.Length = 0;
7516 
7517             queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
7518                                   RTL_QUERY_REGISTRY_DIRECT |
7519                                   RTL_QUERY_REGISTRY_TYPECHECK;
7520             queryTable[0].Name = valueName;
7521             queryTable[0].EntryContext = &driveLetterName;
7522             queryTable->DefaultType = (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
7523 
7524             status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
7525                                             L"\\Registry\\Machine\\System\\DISK",
7526                                             queryTable, NULL, NULL);
7527 
7528             if (!NT_SUCCESS(status)) {
7529                 FREE_POOL(valueName);
7530                 break;
7531             }
7532 
7533             if (driveLetterName.Length == 4 &&
7534                 driveLetterName.Buffer[0] == '%' &&
7535                 driveLetterName.Buffer[1] == ':') {
7536 
7537                 driveLetterName.Buffer[0] = 0xFF;
7538 
7539             } else if (driveLetterName.Length != 4 ||
7540                 driveLetterName.Buffer[0] < FirstDriveLetter ||
7541                 driveLetterName.Buffer[0] > LastDriveLetter ||
7542                 driveLetterName.Buffer[1] != ':') {
7543 
7544                 status = STATUS_NOT_FOUND;
7545                 FREE_POOL(valueName);
7546                 break;
7547             }
7548 
7549             suggestedName = Irp->AssociatedIrp.SystemBuffer;
7550             RtlZeroMemory(suggestedName, sizeof(MOUNTDEV_SUGGESTED_LINK_NAME));
7551             suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
7552             suggestedName->NameLength = 28;
7553 
7554             Irp->IoStatus.Information =
7555                     FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
7556 
7557             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7558                 Irp->IoStatus.Information) {
7559 
7560                 Irp->IoStatus.Information =
7561                         sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
7562                 status = STATUS_BUFFER_OVERFLOW;
7563                 FREE_POOL(valueName);
7564                 break;
7565             }
7566 
7567             RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
7568                                    L"\\Registry\\Machine\\System\\DISK",
7569                                    valueName);
7570 
7571             FREE_POOL(valueName);
7572 
7573             RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
7574             suggestedName->Name[12] = driveLetterName.Buffer[0];
7575             suggestedName->Name[13] = ':';
7576 
7577             //
7578             // NT_SUCCESS(status) based on RtlQueryRegistryValues
7579             //
7580             status = STATUS_SUCCESS;
7581 
7582             break;
7583         }
7584 
7585         default:
7586             status = STATUS_PENDING;
7587             break;
7588     }
7589 
7590     if (status != STATUS_PENDING) {
7591         ClassReleaseRemoveLock(DeviceObject, Irp);
7592         Irp->IoStatus.Status = status;
7593 
7594 
7595         IoCompleteRequest(Irp, IO_NO_INCREMENT);
7596         return status;
7597     }
7598 
7599     if (commonExtension->IsFdo){
7600 
7601         PULONG_PTR function;
7602         PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
7603         size_t sizeNeeded;
7604 
7605         //
7606         // Allocate a SCSI SRB for handling various IOCTLs.
7607         // NOTE - there is a case where an IOCTL is sent to classpnp before AdapterDescriptor
7608         // is initialized. In this case, default to legacy SRB.
7609         //
7610         if ((fdoExtension->AdapterDescriptor != NULL) &&
7611             (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
7612             sizeNeeded = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
7613         } else {
7614             sizeNeeded = sizeof(SCSI_REQUEST_BLOCK);
7615         }
7616 
7617         srb = ExAllocatePoolWithTag(NonPagedPoolNx,
7618                              sizeNeeded +
7619                              (sizeof(ULONG_PTR) * 2),
7620                              '9CcS');
7621 
7622         if (srb == NULL) {
7623 
7624             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
7625             ClassReleaseRemoveLock(DeviceObject, Irp);
7626             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7627             status = STATUS_INSUFFICIENT_RESOURCES;
7628             goto SetStatusAndReturn;
7629         }
7630 
7631         if ((fdoExtension->AdapterDescriptor != NULL) &&
7632             (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
7633             status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srb,
7634                                                    STORAGE_ADDRESS_TYPE_BTL8,
7635                                                    CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
7636                                                    1,
7637                                                    SrbExDataTypeScsiCdb16);
7638             if (NT_SUCCESS(status)) {
7639                 ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
7640                 function = (PULONG_PTR)((PCHAR)srb + sizeNeeded);
7641             } else {
7642                 //
7643                 // Should not occur.
7644                 //
7645                 NT_ASSERT(FALSE);
7646                 goto SetStatusAndReturn;
7647             }
7648         } else {
7649             RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
7650             srb->Length = sizeof(SCSI_REQUEST_BLOCK);
7651             srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
7652             function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
7653         }
7654 
7655         //
7656         // Save the function code and the device object in the memory after
7657         // the SRB.
7658         //
7659 
7660         *function = (ULONG_PTR) DeviceObject;
7661         function++;
7662         *function = (ULONG_PTR) controlCode;
7663 
7664     } else {
7665         srb = NULL;
7666     }
7667 
7668     //
7669     // Change the device type to storage for the switch statement, but only
7670     // if from a legacy device type
7671     //
7672 
7673     if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE  << 16)) ||
7674         ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE  << 16)) ||
7675         ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
7676         ) {
7677 
7678         modifiedIoControlCode = (controlCode & ~0xffff0000);
7679         modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
7680 
7681     } else {
7682 
7683         modifiedIoControlCode = controlCode;
7684 
7685     }
7686 
7687     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
7688 
7689 
7690     switch (modifiedIoControlCode) {
7691 
7692         case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
7693 
7694             FREE_POOL(srb);
7695 
7696             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7697                 sizeof(STORAGE_HOTPLUG_INFO)) {
7698 
7699                 //
7700                 // Indicate unsuccessful status and no data transferred.
7701                 //
7702 
7703                 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
7704                 Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
7705 
7706                 ClassReleaseRemoveLock(DeviceObject, Irp);
7707                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7708                 status = STATUS_BUFFER_TOO_SMALL;
7709 
7710             } else if (!commonExtension->IsFdo) {
7711 
7712 
7713                 //
7714                 // Just forward this down and return
7715                 //
7716 
7717                 IoCopyCurrentIrpStackLocationToNext(Irp);
7718 
7719                 ClassReleaseRemoveLock(DeviceObject, Irp);
7720                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
7721 
7722             } else {
7723 
7724                 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
7725                 PSTORAGE_HOTPLUG_INFO info;
7726 
7727                 fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
7728                 info = Irp->AssociatedIrp.SystemBuffer;
7729 
7730                 *info = fdoExtension->PrivateFdoData->HotplugInfo;
7731                 Irp->IoStatus.Status = STATUS_SUCCESS;
7732                 Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
7733                 ClassReleaseRemoveLock(DeviceObject, Irp);
7734                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7735                 status = STATUS_SUCCESS;
7736             }
7737             break;
7738         }
7739 
7740         case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
7741 
7742             FREE_POOL(srb);
7743 
7744             if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
7745                 sizeof(STORAGE_HOTPLUG_INFO)) {
7746 
7747                 //
7748                 // Indicate unsuccessful status and no data transferred.
7749                 //
7750 
7751                 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
7752 
7753                 ClassReleaseRemoveLock(DeviceObject, Irp);
7754                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7755                 status = STATUS_INFO_LENGTH_MISMATCH;
7756                 goto SetStatusAndReturn;
7757 
7758             }
7759 
7760             if (!commonExtension->IsFdo) {
7761 
7762 
7763                 //
7764                 // Just forward this down and return
7765                 //
7766 
7767                 IoCopyCurrentIrpStackLocationToNext(Irp);
7768 
7769                 ClassReleaseRemoveLock(DeviceObject, Irp);
7770                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
7771 
7772             } else {
7773 
7774                 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
7775                 PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
7776 
7777                 status = STATUS_SUCCESS;
7778 
7779                 if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
7780                 {
7781                     status = STATUS_INVALID_PARAMETER_1;
7782                 }
7783 
7784                 if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
7785                 {
7786                     status = STATUS_INVALID_PARAMETER_2;
7787                 }
7788 
7789                 if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
7790                 {
7791                     status = STATUS_INVALID_PARAMETER_3;
7792                 }
7793 
7794                 if (NT_SUCCESS(status))
7795                 {
7796                     if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
7797                     {
7798                         fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride = info->WriteCacheEnableOverride;
7799 
7800                         //
7801                         // Store the user-defined override in the registry
7802                         //
7803 
7804                         ClassSetDeviceParameter(fdoExtension,
7805                                                 CLASSP_REG_SUBKEY_NAME,
7806                                                 CLASSP_REG_WRITE_CACHE_VALUE_NAME,
7807                                                 info->WriteCacheEnableOverride);
7808                     }
7809 
7810                     fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
7811 
7812                     //
7813                     // Store the user-defined override in the registry
7814                     //
7815 
7816                     ClassSetDeviceParameter(fdoExtension,
7817                                             CLASSP_REG_SUBKEY_NAME,
7818                                             CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
7819                                             (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
7820                 }
7821 
7822                 Irp->IoStatus.Status = status;
7823 
7824                 ClassReleaseRemoveLock(DeviceObject, Irp);
7825                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7826             }
7827 
7828             break;
7829         }
7830 
7831         case IOCTL_STORAGE_CHECK_VERIFY:
7832         case IOCTL_STORAGE_CHECK_VERIFY2: {
7833 
7834             PIRP irp2 = NULL;
7835             PIO_STACK_LOCATION newStack;
7836 
7837             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
7838 
7839             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceIoControl: Check verify\n"));
7840 
7841             //
7842             // If a buffer for a media change count was provided, make sure it's
7843             // big enough to hold the result
7844             //
7845 
7846             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
7847 
7848                 //
7849                 // If the buffer is too small to hold the media change count
7850                 // then return an error to the caller
7851                 //
7852 
7853                 if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
7854                     sizeof(ULONG)) {
7855 
7856                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "DeviceIoControl: media count "
7857                                                                        "buffer too small\n"));
7858 
7859                     Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
7860                     Irp->IoStatus.Information = sizeof(ULONG);
7861 
7862                     FREE_POOL(srb);
7863 
7864                     ClassReleaseRemoveLock(DeviceObject, Irp);
7865                     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7866 
7867                     status = STATUS_BUFFER_TOO_SMALL;
7868                     goto SetStatusAndReturn;
7869                 }
7870             }
7871 
7872             if (!commonExtension->IsFdo) {
7873 
7874 
7875                 //
7876                 // If this is a PDO then we should just forward the request down
7877                 //
7878                 NT_ASSERT(!srb);
7879 
7880                 IoCopyCurrentIrpStackLocationToNext(Irp);
7881 
7882                 ClassReleaseRemoveLock(DeviceObject, Irp);
7883 
7884                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
7885 
7886                 goto SetStatusAndReturn;
7887 
7888             } else {
7889 
7890                 fdoExtension = DeviceObject->DeviceExtension;
7891 
7892             }
7893 
7894             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
7895 
7896                 //
7897                 // The caller has provided a valid buffer.  Allocate an additional
7898                 // irp and stick the CheckVerify completion routine on it.  We will
7899                 // then send this down to the port driver instead of the irp the
7900                 // caller sent in
7901                 //
7902 
7903                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceIoControl: Check verify wants "
7904                                                                        "media count\n"));
7905 
7906                 //
7907                 // Allocate a new irp to send the TestUnitReady to the port driver
7908                 //
7909 
7910                 irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
7911 
7912                 if (irp2 == NULL) {
7913                     Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
7914                     Irp->IoStatus.Information = 0;
7915                     FREE_POOL(srb);
7916                     ClassReleaseRemoveLock(DeviceObject, Irp);
7917                     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
7918                     status = STATUS_INSUFFICIENT_RESOURCES;
7919                     goto SetStatusAndReturn;
7920 
7921                     break;
7922                 }
7923 
7924                 //
7925                 // Make sure to acquire the lock for the new irp.
7926                 //
7927 
7928                 ClassAcquireRemoveLock(DeviceObject, irp2);
7929 
7930                 irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
7931                 IoSetNextIrpStackLocation(irp2);
7932 
7933                 //
7934                 // Set the top stack location and shove the master Irp into the
7935                 // top location
7936                 //
7937 
7938                 newStack = IoGetCurrentIrpStackLocation(irp2);
7939                 newStack->Parameters.Others.Argument1 = Irp;
7940                 newStack->DeviceObject = DeviceObject;
7941 
7942                 //
7943                 // Stick the check verify completion routine onto the stack
7944                 // and prepare the irp for the port driver
7945                 //
7946 
7947                 IoSetCompletionRoutine(irp2,
7948                                        ClassCheckVerifyComplete,
7949                                        NULL,
7950                                        TRUE,
7951                                        TRUE,
7952                                        TRUE);
7953 
7954                 IoSetNextIrpStackLocation(irp2);
7955                 newStack = IoGetCurrentIrpStackLocation(irp2);
7956                 newStack->DeviceObject = DeviceObject;
7957                 newStack->MajorFunction = irpStack->MajorFunction;
7958                 newStack->MinorFunction = irpStack->MinorFunction;
7959                 newStack->Flags = irpStack->Flags;
7960 
7961 
7962                 //
7963                 // Mark the master irp as pending - whether the lower level
7964                 // driver completes it immediately or not this should allow it
7965                 // to go all the way back up.
7966                 //
7967 
7968                 IoMarkIrpPending(Irp);
7969 
7970                 Irp = irp2;
7971 
7972             }
7973 
7974             //
7975             // Test Unit Ready
7976             //
7977 
7978             SrbSetCdbLength(srb, 6);
7979             cdb = SrbGetCdb(srb);
7980             cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
7981 
7982             //
7983             // Set timeout value.
7984             //
7985 
7986             SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
7987 
7988             //
7989             // If this was a CV2 then mark the request as low-priority so we don't
7990             // spin up the drive just to satisfy it.
7991             //
7992 
7993             if (controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
7994                 SrbSetSrbFlags(srb, SRB_CLASS_FLAGS_LOW_PRIORITY);
7995             }
7996 
7997             //
7998             // Since this routine will always hand the request to the
7999             // port driver if there isn't a data transfer to be done
8000             // we don't have to worry about completing the request here
8001             // on an error
8002             //
8003 
8004             //
8005             // This routine uses a completion routine so we don't want to release
8006             // the remove lock until then.
8007             //
8008 
8009             status = ClassSendSrbAsynchronous(DeviceObject,
8010                                               srb,
8011                                               Irp,
8012                                               NULL,
8013                                               0,
8014                                               FALSE);
8015 
8016             break;
8017         }
8018 
8019         case IOCTL_STORAGE_MEDIA_REMOVAL:
8020         case IOCTL_STORAGE_EJECTION_CONTROL: {
8021 
8022             PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
8023 
8024             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,  "DiskIoControl: ejection control\n"));
8025 
8026             FREE_POOL(srb);
8027 
8028             if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
8029                 sizeof(PREVENT_MEDIA_REMOVAL)) {
8030 
8031                 //
8032                 // Indicate unsuccessful status and no data transferred.
8033                 //
8034 
8035                 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
8036 
8037                 ClassReleaseRemoveLock(DeviceObject, Irp);
8038                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8039                 status = STATUS_INFO_LENGTH_MISMATCH;
8040                 goto SetStatusAndReturn;
8041             }
8042 
8043             if (!commonExtension->IsFdo) {
8044 
8045 
8046                 //
8047                 // Just forward this down and return
8048                 //
8049 
8050                 IoCopyCurrentIrpStackLocationToNext(Irp);
8051 
8052                 ClassReleaseRemoveLock(DeviceObject, Irp);
8053                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8054             }
8055             else {
8056 
8057                 // i don't believe this assertion is valid.  this is a request
8058                 // from user-mode, so they could request this for any device
8059                 // they want?  also, we handle it properly.
8060                 // NT_ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
8061                 status = ClasspEjectionControl(
8062                             DeviceObject,
8063                             Irp,
8064                             ((modifiedIoControlCode ==
8065                             IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
8066                                                               SimpleMediaLock),
8067                             mediaRemoval->PreventMediaRemoval);
8068 
8069                 Irp->IoStatus.Status = status;
8070                 ClassReleaseRemoveLock(DeviceObject, Irp);
8071                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8072             }
8073 
8074             break;
8075         }
8076 
8077         case IOCTL_STORAGE_MCN_CONTROL: {
8078 
8079             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,  "DiskIoControl: MCN control\n"));
8080 
8081             if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
8082                 sizeof(PREVENT_MEDIA_REMOVAL)) {
8083 
8084                 //
8085                 // Indicate unsuccessful status and no data transferred.
8086                 //
8087 
8088                 Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
8089                 Irp->IoStatus.Information = 0;
8090 
8091                 FREE_POOL(srb);
8092 
8093                 ClassReleaseRemoveLock(DeviceObject, Irp);
8094                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8095                 status = STATUS_INFO_LENGTH_MISMATCH;
8096                 goto SetStatusAndReturn;
8097             }
8098 
8099             if (!commonExtension->IsFdo) {
8100 
8101 
8102                 //
8103                 // Just forward this down and return
8104                 //
8105 
8106                 FREE_POOL(srb);
8107 
8108                 IoCopyCurrentIrpStackLocationToNext(Irp);
8109 
8110                 ClassReleaseRemoveLock(DeviceObject, Irp);
8111                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8112 
8113             } else {
8114 
8115                 //
8116                 // Call to the FDO - handle the ejection control.
8117                 //
8118 
8119                 status = ClasspMcnControl(DeviceObject->DeviceExtension,
8120                                           Irp,
8121                                           srb);
8122             }
8123             goto SetStatusAndReturn;
8124         }
8125 
8126         case IOCTL_STORAGE_RESERVE:
8127         case IOCTL_STORAGE_RELEASE: {
8128 
8129             //
8130             // Reserve logical unit.
8131             //
8132 
8133             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
8134 
8135             if (!commonExtension->IsFdo) {
8136 
8137 
8138                 IoCopyCurrentIrpStackLocationToNext(Irp);
8139 
8140                 ClassReleaseRemoveLock(DeviceObject, Irp);
8141                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8142                 goto SetStatusAndReturn;
8143 
8144             } else {
8145                 fdoExtension = DeviceObject->DeviceExtension;
8146             }
8147 
8148             if (TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6))
8149             {
8150                 SrbSetCdbLength(srb, 10);
8151                 cdb = SrbGetCdb(srb);
8152                 cdb->CDB10.OperationCode = (modifiedIoControlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
8153             }
8154             else
8155             {
8156                 SrbSetCdbLength(srb, 6);
8157                 cdb = SrbGetCdb(srb);
8158                 cdb->CDB6GENERIC.OperationCode = (modifiedIoControlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT : SCSIOP_RELEASE_UNIT;
8159             }
8160 
8161             //
8162             // Set timeout value.
8163             //
8164 
8165             SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
8166 
8167             //
8168             // Send reserves as tagged requests.
8169             //
8170 
8171             if ( IOCTL_STORAGE_RESERVE == modifiedIoControlCode ) {
8172                 SrbSetSrbFlags(srb, SRB_FLAGS_QUEUE_ACTION_ENABLE);
8173                 SrbSetRequestAttribute(srb, SRB_SIMPLE_TAG_REQUEST);
8174             }
8175 
8176             status = ClassSendSrbAsynchronous(DeviceObject,
8177                                               srb,
8178                                               Irp,
8179                                               NULL,
8180                                               0,
8181                                               FALSE);
8182 
8183             break;
8184         }
8185 
8186         case IOCTL_STORAGE_PERSISTENT_RESERVE_IN:
8187         case IOCTL_STORAGE_PERSISTENT_RESERVE_OUT: {
8188 
8189             if (!commonExtension->IsFdo) {
8190 
8191                 IoCopyCurrentIrpStackLocationToNext(Irp);
8192 
8193                 ClassReleaseRemoveLock(DeviceObject, Irp);
8194                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8195                 goto SetStatusAndReturn;
8196             }
8197 
8198             //
8199             // Process Persistent Reserve
8200             //
8201 
8202             status = ClasspPersistentReserve(DeviceObject, Irp, srb);
8203 
8204             break;
8205 
8206         }
8207 
8208         case IOCTL_STORAGE_EJECT_MEDIA:
8209         case IOCTL_STORAGE_LOAD_MEDIA:
8210         case IOCTL_STORAGE_LOAD_MEDIA2:{
8211 
8212             //
8213             // Eject media.
8214             //
8215 
8216             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
8217 
8218             if (!commonExtension->IsFdo) {
8219 
8220 
8221                 IoCopyCurrentIrpStackLocationToNext(Irp);
8222 
8223                 ClassReleaseRemoveLock(DeviceObject, Irp);
8224 
8225                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8226                 goto SetStatusAndReturn;
8227             } else {
8228                 fdoExtension = DeviceObject->DeviceExtension;
8229             }
8230 
8231             if (commonExtension->PagingPathCount != 0) {
8232 
8233                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,  "ClassDeviceControl: call to eject paging device - "
8234                                                                     "failure\n"));
8235 
8236                 status = STATUS_FILES_OPEN;
8237                 Irp->IoStatus.Status = status;
8238 
8239                 Irp->IoStatus.Information = 0;
8240 
8241                 FREE_POOL(srb);
8242 
8243                 ClassReleaseRemoveLock(DeviceObject, Irp);
8244                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8245                 goto SetStatusAndReturn;
8246             }
8247 
8248             //
8249             // Synchronize with ejection control and ejection cleanup code as
8250             // well as other eject/load requests.
8251             //
8252 
8253             KeEnterCriticalRegion();
8254             (VOID)KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
8255                                         UserRequest,
8256                                         KernelMode,
8257                                         FALSE,
8258                                         NULL);
8259 
8260             if (fdoExtension->ProtectedLockCount != 0) {
8261 
8262                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,  "ClassDeviceControl: call to eject protected locked "
8263                                                                     "device - failure\n"));
8264 
8265                 status = STATUS_DEVICE_BUSY;
8266                 Irp->IoStatus.Status = status;
8267                 Irp->IoStatus.Information = 0;
8268 
8269                 FREE_POOL(srb);
8270 
8271                 ClassReleaseRemoveLock(DeviceObject, Irp);
8272                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8273 
8274                 KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
8275                            IO_NO_INCREMENT,
8276                            FALSE);
8277                 KeLeaveCriticalRegion();
8278 
8279                 goto SetStatusAndReturn;
8280             }
8281 
8282             SrbSetCdbLength(srb, 6);
8283             cdb = SrbGetCdb(srb);
8284 
8285             cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
8286             cdb->START_STOP.LoadEject = 1;
8287 
8288             if (modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
8289                 cdb->START_STOP.Start = 0;
8290             } else {
8291                 cdb->START_STOP.Start = 1;
8292             }
8293 
8294             //
8295             // Set timeout value.
8296             //
8297 
8298             SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
8299             status = ClassSendSrbAsynchronous(DeviceObject,
8300                                                   srb,
8301                                                   Irp,
8302                                                   NULL,
8303                                                   0,
8304                                                   FALSE);
8305 
8306             KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
8307             KeLeaveCriticalRegion();
8308 
8309             break;
8310         }
8311 
8312         case IOCTL_STORAGE_FIND_NEW_DEVICES: {
8313 
8314             FREE_POOL(srb);
8315 
8316             if (commonExtension->IsFdo) {
8317 
8318                 IoInvalidateDeviceRelations(
8319                     ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
8320                     BusRelations);
8321 
8322                 status = STATUS_SUCCESS;
8323                 Irp->IoStatus.Status = status;
8324 
8325                 ClassReleaseRemoveLock(DeviceObject, Irp);
8326                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8327             }
8328             else {
8329 
8330 
8331                 IoCopyCurrentIrpStackLocationToNext(Irp);
8332 
8333                 ClassReleaseRemoveLock(DeviceObject, Irp);
8334                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8335             }
8336             break;
8337         }
8338 
8339         case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
8340 
8341             FREE_POOL(srb);
8342 
8343             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
8344                 sizeof(STORAGE_DEVICE_NUMBER)) {
8345 
8346                 PSTORAGE_DEVICE_NUMBER deviceNumber =
8347                     Irp->AssociatedIrp.SystemBuffer;
8348                 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
8349                     commonExtension->PartitionZeroExtension;
8350 
8351                 deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
8352                 deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
8353                 deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
8354 
8355                 status = STATUS_SUCCESS;
8356                 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
8357 
8358             } else {
8359                 status = STATUS_BUFFER_TOO_SMALL;
8360                 Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
8361             }
8362 
8363             Irp->IoStatus.Status = status;
8364             ClassReleaseRemoveLock(DeviceObject, Irp);
8365             ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8366 
8367             break;
8368         }
8369 
8370 
8371         case IOCTL_STORAGE_READ_CAPACITY: {
8372 
8373             FREE_POOL(srb);
8374 
8375             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
8376                 sizeof(STORAGE_READ_CAPACITY)) {
8377 
8378                 //
8379                 // Indicate unsuccessful status and no data transferred.
8380                 //
8381 
8382                 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
8383                 Irp->IoStatus.Information = sizeof(STORAGE_READ_CAPACITY);
8384 
8385                 ClassReleaseRemoveLock(DeviceObject, Irp);
8386                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8387                 status = STATUS_BUFFER_TOO_SMALL;
8388                 break;
8389             }
8390 
8391             if (!commonExtension->IsFdo) {
8392 
8393 
8394                 //
8395                 // Just forward this down and return
8396                 //
8397 
8398                 IoCopyCurrentIrpStackLocationToNext(Irp);
8399 
8400                 ClassReleaseRemoveLock(DeviceObject, Irp);
8401                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8402             }
8403             else {
8404 
8405                 PFUNCTIONAL_DEVICE_EXTENSION fdoExt = DeviceObject->DeviceExtension;
8406                 PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
8407                 PSTORAGE_READ_CAPACITY readCapacity = Irp->AssociatedIrp.SystemBuffer;
8408                 LARGE_INTEGER diskLength;
8409 
8410                 status = ClassReadDriveCapacity(DeviceObject);
8411                 if (NT_SUCCESS(status) && fdoData->IsCachedDriveCapDataValid) {
8412 
8413                     readCapacity->Version = sizeof(STORAGE_READ_CAPACITY);
8414                     readCapacity->Size = sizeof(STORAGE_READ_CAPACITY);
8415 
8416                     REVERSE_BYTES(&readCapacity->BlockLength,
8417                                         &fdoData->LastKnownDriveCapacityData.BytesPerBlock);
8418                     REVERSE_BYTES_QUAD(&readCapacity->NumberOfBlocks,
8419                                        &fdoData->LastKnownDriveCapacityData.LogicalBlockAddress);
8420                     readCapacity->NumberOfBlocks.QuadPart++;
8421 
8422                     readCapacity->DiskLength = fdoExt->CommonExtension.PartitionLength;
8423 
8424                     //
8425                     // Make sure the lengths are equal.
8426                     // Remove this after testing.
8427                     //
8428                     diskLength.QuadPart = readCapacity->NumberOfBlocks.QuadPart *
8429                                             readCapacity->BlockLength;
8430 
8431                     Irp->IoStatus.Status = STATUS_SUCCESS;
8432                     Irp->IoStatus.Information = sizeof(STORAGE_READ_CAPACITY);
8433 
8434                 } else {
8435                     //
8436                     // Read capacity request failed.
8437                     //
8438                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,  "ClassDeviceControl: ClassReadDriveCapacity failed: 0x%X IsCachedDriveCapDataValid: %d\n",
8439                                    status, fdoData->IsCachedDriveCapDataValid));
8440                     Irp->IoStatus.Status = status;
8441                     Irp->IoStatus.Information = 0;
8442                 }
8443                 ClassReleaseRemoveLock(DeviceObject, Irp);
8444                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8445             }
8446 
8447             break;
8448         }
8449 
8450         case IOCTL_STORAGE_QUERY_PROPERTY: {
8451 
8452             PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
8453 
8454             if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_PROPERTY_QUERY)) {
8455 
8456                 status = STATUS_INFO_LENGTH_MISMATCH;
8457                 Irp->IoStatus.Status = status;
8458                 ClassReleaseRemoveLock(DeviceObject, Irp);
8459                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8460                 FREE_POOL(srb);
8461                 break;
8462             }
8463 
8464             if (!commonExtension->IsFdo) {
8465 
8466 
8467                 IoCopyCurrentIrpStackLocationToNext(Irp);
8468 
8469                 ClassReleaseRemoveLock(DeviceObject, Irp);
8470                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8471                 FREE_POOL(srb);
8472                 break;
8473             }
8474 
8475             //
8476             // Determine PropertyId type and either call appropriate routine
8477             // or pass request to lower drivers.
8478             //
8479 
8480             switch ( query->PropertyId ) {
8481 
8482                 case StorageDeviceUniqueIdProperty: {
8483 
8484                     status = ClasspDuidQueryProperty(DeviceObject, Irp);
8485                     break;
8486                 }
8487 
8488                 case StorageDeviceWriteCacheProperty: {
8489 
8490                     status = ClasspWriteCacheProperty(DeviceObject, Irp, srb);
8491                     break;
8492                 }
8493 
8494                 // these propertyId has been implemented in some port driver and filter drivers.
8495                 // to keep the backwards compatibility, classpnp will send the request down if it's supported by lower layer.
8496                 // otherwise, classpnp sends SCSI command and then interprets the result.
8497                 case StorageAccessAlignmentProperty: {
8498 
8499                     status = ClasspAccessAlignmentProperty(DeviceObject, Irp, srb);
8500                     break;
8501                 }
8502 
8503                 case StorageDeviceSeekPenaltyProperty: {
8504 
8505                     status = ClasspDeviceSeekPenaltyProperty(DeviceObject, Irp, srb);
8506                     break;
8507                 }
8508 
8509                 case StorageDeviceTrimProperty: {
8510 
8511                     status = ClasspDeviceTrimProperty(DeviceObject, Irp, srb);
8512                     break;
8513                 }
8514 
8515                 case StorageDeviceLBProvisioningProperty: {
8516 
8517                     status = ClasspDeviceLBProvisioningProperty(DeviceObject, Irp, srb);
8518                     break;
8519                 }
8520 
8521                 case StorageDeviceCopyOffloadProperty: {
8522 
8523                     status = ClasspDeviceCopyOffloadProperty(DeviceObject, Irp, srb);
8524                     break;
8525                 }
8526 
8527                 case StorageDeviceMediumProductType: {
8528 
8529                     status = ClasspDeviceMediaTypeProperty(DeviceObject, Irp, srb);
8530                     break;
8531                 }
8532 
8533                 default: {
8534 
8535                     //
8536                     // Copy the Irp stack parameters to the next stack location.
8537                     //
8538 
8539                     IoCopyCurrentIrpStackLocationToNext(Irp);
8540 
8541                     ClassReleaseRemoveLock(DeviceObject, Irp);
8542                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8543                     break;
8544                 }
8545             }   // end switch
8546 
8547             FREE_POOL(srb);
8548             break;
8549         }
8550 
8551         case IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT: {
8552 
8553             FREE_POOL(srb);
8554 
8555             if (!commonExtension->IsFdo) {
8556 
8557 
8558                 IoCopyCurrentIrpStackLocationToNext(Irp);
8559 
8560                 ClassReleaseRemoveLock(DeviceObject, Irp);
8561                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8562                 break;
8563             }
8564 
8565             //
8566             // Process priority hit request
8567             //
8568 
8569             status = ClasspPriorityHint(DeviceObject, Irp);
8570             break;
8571         }
8572 
8573         case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: {
8574 
8575             PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
8576 
8577             if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) {
8578 
8579                 status = STATUS_INVALID_PARAMETER;
8580                 Irp->IoStatus.Status = status;
8581                 ClassReleaseRemoveLock(DeviceObject, Irp);
8582                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8583                 FREE_POOL(srb);
8584                 break;
8585             }
8586 
8587             if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
8588                 (sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES) + dsmAttributes->ParameterBlockLength + dsmAttributes->DataSetRangesLength)) {
8589 
8590                 status = STATUS_INVALID_PARAMETER;
8591                 Irp->IoStatus.Status = status;
8592                 ClassReleaseRemoveLock(DeviceObject, Irp);
8593                 ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8594                 FREE_POOL(srb);
8595                 break;
8596             }
8597 
8598             if (!commonExtension->IsFdo) {
8599 
8600 
8601                 IoCopyCurrentIrpStackLocationToNext(Irp);
8602 
8603                 ClassReleaseRemoveLock(DeviceObject, Irp);
8604                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8605                 FREE_POOL(srb);
8606                 break;
8607             }
8608 
8609             switch(dsmAttributes->Action) {
8610 
8611                 // only process Trim action in class layer if possible.
8612                 case DeviceDsmAction_Trim: {
8613                     status = ClasspDeviceTrimProcess(DeviceObject, Irp, &activityId, srb);
8614                     break;
8615                 }
8616 
8617                 case DeviceDsmAction_OffloadRead: {
8618                     status = ClassDeviceProcessOffloadRead(DeviceObject, Irp, srb);
8619                     break;
8620                 }
8621 
8622                 case DeviceDsmAction_OffloadWrite: {
8623                     status = ClassDeviceProcessOffloadWrite(DeviceObject, Irp, srb);
8624                     break;
8625                 }
8626 
8627                 case DeviceDsmAction_Allocation: {
8628                     status = ClasspDeviceGetLBAStatus(DeviceObject, Irp, srb);
8629                     break;
8630                 }
8631 
8632 
8633                 default: {
8634 
8635 
8636                     //
8637                     // Copy the Irp stack parameters to the next stack location.
8638                     //
8639 
8640                     IoCopyCurrentIrpStackLocationToNext(Irp);
8641 
8642                     ClassReleaseRemoveLock(DeviceObject, Irp);
8643                     status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8644                     break;
8645                 }
8646             }   // end switch
8647 
8648             FREE_POOL(srb);
8649             break;
8650         }
8651 
8652         case IOCTL_STORAGE_GET_LB_PROVISIONING_MAP_RESOURCES: {
8653 
8654             if (commonExtension->IsFdo) {
8655 
8656                 status = ClassDeviceGetLBProvisioningResources(DeviceObject, Irp, srb);
8657 
8658             } else {
8659 
8660                 IoCopyCurrentIrpStackLocationToNext(Irp);
8661 
8662                 ClassReleaseRemoveLock(DeviceObject, Irp);
8663                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8664             }
8665 
8666             FREE_POOL(srb);
8667 
8668             break;
8669         }
8670 
8671         case IOCTL_STORAGE_EVENT_NOTIFICATION: {
8672 
8673             FREE_POOL(srb);
8674 
8675             status = ClasspStorageEventNotification(DeviceObject, Irp);
8676             break;
8677         }
8678 
8679 #if (NTDDI_VERSION >= NTDDI_WINTRHESHOLD)
8680         case IOCTL_STORAGE_FIRMWARE_GET_INFO: {
8681             FREE_POOL(srb);
8682 
8683             status = ClassDeviceHwFirmwareGetInfoProcess(DeviceObject, Irp);
8684             break;
8685         }
8686 
8687         case IOCTL_STORAGE_FIRMWARE_DOWNLOAD: {
8688             if (!commonExtension->IsFdo) {
8689 
8690                 IoCopyCurrentIrpStackLocationToNext(Irp);
8691 
8692                 ClassReleaseRemoveLock(DeviceObject, Irp);
8693                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8694                 goto SetStatusAndReturn;
8695             }
8696 
8697             status = ClassDeviceHwFirmwareDownloadProcess(DeviceObject, Irp, srb);
8698             break;
8699         }
8700 
8701         case IOCTL_STORAGE_FIRMWARE_ACTIVATE: {
8702             if (!commonExtension->IsFdo) {
8703 
8704                 IoCopyCurrentIrpStackLocationToNext(Irp);
8705 
8706                 ClassReleaseRemoveLock(DeviceObject, Irp);
8707                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8708                 goto SetStatusAndReturn;
8709             }
8710 
8711             status = ClassDeviceHwFirmwareActivateProcess(DeviceObject, Irp, srb);
8712             break;
8713         }
8714 #endif
8715 
8716 
8717         default: {
8718 
8719             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
8720                         controlCode, DeviceObject));
8721 
8722 
8723             //
8724             // Pass the device control to the next driver.
8725             //
8726 
8727             FREE_POOL(srb);
8728 
8729             //
8730             // Copy the Irp stack parameters to the next stack location.
8731             //
8732 
8733             IoCopyCurrentIrpStackLocationToNext(Irp);
8734 
8735             ClassReleaseRemoveLock(DeviceObject, Irp);
8736             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
8737             break;
8738         }
8739 
8740     } // end switch( ...
8741 
8742 SetStatusAndReturn:
8743 
8744     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
8745 
8746     return status;
8747 } // end ClassDeviceControl()
8748 
8749 /*++////////////////////////////////////////////////////////////////////////////
8750 
8751 ClassShutdownFlush()
8752 
8753 Routine Description:
8754 
8755     This routine is called for a shutdown and flush IRPs.  These are sent by the
8756     system before it actually shuts down or when the file system does a flush.
8757     If it exists, the device-specific driver's routine will be invoked. If there
8758     wasn't one specified, the Irp will be completed with an Invalid device request.
8759 
8760 Arguments:
8761 
8762     DriverObject - Pointer to device object to being shutdown by system.
8763 
8764     Irp - IRP involved.
8765 
8766 Return Value:
8767 
8768     NT Status
8769 
8770 --*/
8771 NTSTATUS
8772 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
8773 ClassShutdownFlush(
8774     IN PDEVICE_OBJECT DeviceObject,
8775     IN PIRP Irp
8776     )
8777 {
8778     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
8779 
8780     ULONG isRemoved;
8781 
8782     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
8783     _Analysis_assume_(isRemoved);
8784     if(isRemoved) {
8785 
8786         ClassReleaseRemoveLock(DeviceObject, Irp);
8787 
8788         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
8789 
8790         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8791 
8792         return STATUS_DEVICE_DOES_NOT_EXIST;
8793     }
8794 
8795     if (commonExtension->DevInfo->ClassShutdownFlush) {
8796 
8797         //
8798         // Call the device-specific driver's routine.
8799         //
8800 
8801         return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
8802     }
8803 
8804     //
8805     // Device-specific driver doesn't support this.
8806     //
8807 
8808     Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
8809 
8810     ClassReleaseRemoveLock(DeviceObject, Irp);
8811     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
8812 
8813     return STATUS_INVALID_DEVICE_REQUEST;
8814 } // end ClassShutdownFlush()
8815 
8816 /*++////////////////////////////////////////////////////////////////////////////
8817 
8818 ClasspIsPortable()
8819 
8820 Routine Description:
8821 
8822     This routine is called during start device to determine whether the PDO
8823     for a stack reports itself as portable.
8824 
8825 Arguments:
8826 
8827     FdoExtension - Pointer to FDO whose PDO we check for portability.
8828 
8829     IsPortable - Boolean pointer in which to store result.
8830 
8831 Return Value:
8832 
8833     NT Status
8834 
8835 --*/
8836 NTSTATUS
8837 ClasspIsPortable(
8838     _In_ PFUNCTIONAL_DEVICE_EXTENSION   FdoExtension,
8839     _Out_ PBOOLEAN                      IsPortable
8840     )
8841 {
8842     DEVPROP_BOOLEAN            isInternal = DEVPROP_FALSE;
8843     BOOLEAN                    isPortable = FALSE;
8844     ULONG                      size       = 0;
8845     NTSTATUS                   status     = STATUS_SUCCESS;
8846     DEVPROPTYPE                type       = DEVPROP_TYPE_EMPTY;
8847 
8848     PAGED_CODE();
8849 
8850     *IsPortable = FALSE;
8851 
8852     //
8853     // Check to see if the underlying device
8854     // object is in local machine container
8855     //
8856 
8857     status = IoGetDevicePropertyData(FdoExtension->LowerPdo,
8858                                      &DEVPKEY_Device_InLocalMachineContainer,
8859                                      0,
8860                                      0,
8861                                      sizeof(isInternal),
8862                                      &isInternal,
8863                                      &size,
8864                                      &type);
8865 
8866     if (!NT_SUCCESS(status)) {
8867         goto cleanup;
8868     }
8869 
8870     NT_ASSERT(size == sizeof(isInternal));
8871     NT_ASSERT(type == DEVPROP_TYPE_BOOLEAN);
8872 
8873     //
8874     // Volume is hot-pluggable if the disk pdo
8875     // container id differs from that of root device
8876     //
8877 
8878     if (isInternal == DEVPROP_TRUE) {
8879         goto cleanup;
8880     }
8881 
8882     isPortable = TRUE;
8883 
8884     //
8885     // Examine the  bus type to  ensure
8886     // that this really is a fixed disk
8887     //
8888 
8889     if (FdoExtension->DeviceDescriptor->BusType == BusTypeFibre ||
8890         FdoExtension->DeviceDescriptor->BusType == BusTypeiScsi ||
8891         FdoExtension->DeviceDescriptor->BusType == BusTypeRAID) {
8892 
8893         isPortable = FALSE;
8894     }
8895 
8896     *IsPortable = isPortable;
8897 
8898 cleanup:
8899 
8900     return status;
8901 }
8902 
8903 /*++////////////////////////////////////////////////////////////////////////////
8904 
8905 ClassCreateDeviceObject()
8906 
8907 Routine Description:
8908 
8909     This routine creates an object for the physical device specified and
8910     sets up the deviceExtension's function pointers for each entry point
8911     in the device-specific driver.
8912 
8913 Arguments:
8914 
8915     DriverObject - Pointer to driver object created by system.
8916 
8917     ObjectNameBuffer - Dir. name of the object to create.
8918 
8919     LowerDeviceObject - Pointer to the lower device object
8920 
8921     IsFdo - should this be an fdo or a pdo
8922 
8923     DeviceObject - Pointer to the device object pointer we will return.
8924 
8925 Return Value:
8926 
8927     NTSTATUS
8928 
8929 --*/
8930 
8931 _IRQL_requires_max_(PASSIVE_LEVEL)
8932 _Must_inspect_result_
8933 _Post_satisfies_(return <= 0)
8934 SCSIPORT_API
8935 NTSTATUS
8936 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
8937 ClassCreateDeviceObject(
8938     _In_ PDRIVER_OBJECT     DriverObject,
8939     _In_z_ PCCHAR           ObjectNameBuffer,
8940     _In_ PDEVICE_OBJECT     LowerDevice,
8941     _In_ BOOLEAN            IsFdo,
8942     _Outptr_result_nullonfailure_
8943     _At_(*DeviceObject, __drv_allocatesMem(Mem) __drv_aliasesMem)
8944     PDEVICE_OBJECT          *DeviceObject
8945     )
8946 {
8947     BOOLEAN        isPartitionable;
8948     STRING         ntNameString;
8949     UNICODE_STRING ntUnicodeString;
8950     NTSTATUS       status;
8951     PDEVICE_OBJECT deviceObject = NULL;
8952 
8953     ULONG          characteristics;
8954     SIZE_T         rundownSize = ExSizeOfRundownProtectionCacheAware();
8955     PCHAR          rundownAddr = NULL;
8956     ULONG          devExtSize;
8957 
8958 #ifdef _MSC_VER
8959 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
8960 #endif
8961     PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
8962 
8963     PCLASS_DEV_INFO devInfo;
8964 
8965     PAGED_CODE();
8966 
8967     _Analysis_assume_(driverExtension != NULL);
8968 
8969     *DeviceObject = NULL;
8970     RtlInitUnicodeString(&ntUnicodeString, NULL);
8971 
8972     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,  "ClassCreateFdo: Create device object\n"));
8973 
8974     NT_ASSERT(LowerDevice);
8975 
8976     //
8977     // Make sure that if we're making PDO's we have an enumeration routine
8978     //
8979 
8980     isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
8981 
8982     NT_ASSERT(IsFdo || isPartitionable);
8983 
8984     //
8985     // Grab the correct dev-info structure out of the init data
8986     //
8987 
8988     if (IsFdo) {
8989         devInfo = &(driverExtension->InitData.FdoData);
8990     } else {
8991         devInfo = &(driverExtension->InitData.PdoData);
8992     }
8993 
8994     characteristics = devInfo->DeviceCharacteristics;
8995 
8996     if (ARGUMENT_PRESENT(ObjectNameBuffer)) {
8997         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,  "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
8998 
8999         RtlInitString(&ntNameString, ObjectNameBuffer);
9000 
9001         status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
9002 
9003         if (!NT_SUCCESS(status)) {
9004 
9005             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
9006                         "ClassCreateFdo: Cannot convert string %s\n",
9007                         ObjectNameBuffer));
9008 
9009             ntUnicodeString.Buffer = NULL;
9010             return status;
9011         }
9012     } else {
9013         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,  "ClassCreateFdo: Object will be unnamed\n"));
9014 
9015         if (IsFdo == FALSE) {
9016 
9017             //
9018             // PDO's have to have some sort of name.
9019             //
9020 
9021             SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
9022         }
9023 
9024         RtlInitUnicodeString(&ntUnicodeString, NULL);
9025     }
9026 
9027     devExtSize = devInfo->DeviceExtensionSize +
9028         (ULONG)sizeof(CLASS_PRIVATE_COMMON_DATA) + (ULONG)rundownSize;
9029     status = IoCreateDevice(DriverObject,
9030                             devExtSize,
9031                             &ntUnicodeString,
9032                             devInfo->DeviceType,
9033                             characteristics,
9034                             FALSE,
9035                             &deviceObject);
9036 
9037     if (!NT_SUCCESS(status)) {
9038 
9039         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "ClassCreateFdo: Can not create device object %lx\n",
9040                     status));
9041         NT_ASSERT(deviceObject == NULL);
9042 
9043         //
9044         // buffer is not used any longer here.
9045         //
9046 
9047         if (ntUnicodeString.Buffer != NULL) {
9048             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,  "ClassCreateFdo: Freeing unicode name buffer\n"));
9049             FREE_POOL(ntUnicodeString.Buffer);
9050             RtlInitUnicodeString(&ntUnicodeString, NULL);
9051         }
9052 
9053     } else {
9054 
9055         PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
9056 
9057         RtlZeroMemory(
9058             deviceObject->DeviceExtension,
9059             devExtSize);
9060 
9061         //
9062         // Setup version code
9063         //
9064 
9065         commonExtension->Version = 0x03;
9066 
9067         //
9068         // Setup the remove lock and event
9069         //
9070 
9071         commonExtension->IsRemoved = NO_REMOVE;
9072 
9073 #if DBG
9074 
9075         commonExtension->RemoveLock = 0;
9076 
9077 #endif
9078 
9079         KeInitializeEvent(&commonExtension->RemoveEvent,
9080                           SynchronizationEvent,
9081                           FALSE);
9082 
9083 
9084         ClasspInitializeRemoveTracking(deviceObject);
9085 
9086         //
9087         // Initialize the PrivateCommonData
9088         //
9089 
9090         commonExtension->PrivateCommonData = (PCLASS_PRIVATE_COMMON_DATA)
9091             ((PCHAR)deviceObject->DeviceExtension + devInfo->DeviceExtensionSize);
9092         rundownAddr = (PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA);
9093         ExInitializeRundownProtectionCacheAware((PEX_RUNDOWN_REF_CACHE_AWARE)rundownAddr, rundownSize);
9094         commonExtension->PrivateCommonData->RemoveLockFailAcquire = 0;
9095 
9096         //
9097         // Acquire the lock once.  This reference will be released when the
9098         // remove IRP has been received.
9099         //
9100 
9101         ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
9102 
9103         //
9104         // Store a pointer to the driver extension so we don't have to do
9105         // lookups to get it.
9106         //
9107 
9108         commonExtension->DriverExtension = driverExtension;
9109 
9110         //
9111         // Fill in entry points
9112         //
9113 
9114         commonExtension->DevInfo = devInfo;
9115 
9116         //
9117         // Initialize some of the common values in the structure
9118         //
9119 
9120         commonExtension->DeviceObject = deviceObject;
9121 
9122         commonExtension->LowerDeviceObject = NULL;
9123 
9124         commonExtension->DispatchTable = driverExtension->DeviceMajorFunctionTable;
9125 
9126         if(IsFdo) {
9127 
9128             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
9129 
9130             commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
9131 
9132             //
9133             // Set the initial device object flags.
9134             //
9135 
9136             SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
9137             //
9138             // Clear the PDO list
9139             //
9140 
9141             commonExtension->ChildList = NULL;
9142 
9143             commonExtension->DriverData =
9144                 ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
9145 
9146             //
9147             // The disk class driver creates only FDO. The partition number
9148             // for the FDO must be 0.
9149             //
9150 
9151             if ((isPartitionable == TRUE) ||
9152                 (devInfo->DeviceType == FILE_DEVICE_DISK)) {
9153 
9154                 commonExtension->PartitionNumber = 0;
9155             } else {
9156                 commonExtension->PartitionNumber = (ULONG) (-1L);
9157             }
9158 
9159             fdoExtension->DevicePowerState = PowerDeviceD0;
9160 
9161             KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
9162                               SynchronizationEvent,
9163                               TRUE);
9164 
9165             KeInitializeEvent(&fdoExtension->ChildLock,
9166                               SynchronizationEvent,
9167                               TRUE);
9168 
9169             status = ClasspAllocateReleaseRequest(deviceObject);
9170 
9171             if(!NT_SUCCESS(status)) {
9172                 IoDeleteDevice(deviceObject);
9173                 *DeviceObject = NULL;
9174 
9175                 if (ntUnicodeString.Buffer != NULL) {
9176                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,  "ClassCreateFdo: Freeing unicode name buffer\n"));
9177                     FREE_POOL(ntUnicodeString.Buffer);
9178                     RtlInitUnicodeString(&ntUnicodeString, NULL);
9179                 }
9180 
9181                 return status;
9182             }
9183 
9184         } else {
9185 
9186             PPHYSICAL_DEVICE_EXTENSION pdoExtension =
9187                 deviceObject->DeviceExtension;
9188 
9189             PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
9190                 LowerDevice->DeviceExtension;
9191 
9192             SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
9193 
9194             commonExtension->PartitionZeroExtension = p0Extension;
9195 
9196             //
9197             // Stick this onto the PDO list
9198             //
9199 
9200             ClassAddChild(p0Extension, pdoExtension, TRUE);
9201 
9202             commonExtension->DriverData = (PVOID) (pdoExtension + 1);
9203 
9204             //
9205             // Get the top of stack for the lower device - this allows
9206             // filters to get stuck in between the partitions and the
9207             // physical disk.
9208             //
9209 
9210             commonExtension->LowerDeviceObject =
9211                 IoGetAttachedDeviceReference(LowerDevice);
9212 
9213             //
9214             // Pnp will keep a reference to the lower device object long
9215             // after this partition has been deleted.  Dereference now so
9216             // we don't have to deal with it later.
9217             //
9218 
9219             ObDereferenceObject(commonExtension->LowerDeviceObject);
9220         }
9221 
9222         KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
9223 
9224         commonExtension->IsFdo = IsFdo;
9225 
9226         commonExtension->DeviceName = ntUnicodeString;
9227 
9228         commonExtension->PreviousState = 0xff;
9229 
9230         InitializeDictionary(&(commonExtension->FileObjectDictionary));
9231 
9232         commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
9233 
9234         if (commonExtension->DriverExtension->InitData.ClassStartIo) {
9235             IoSetStartIoAttributes(deviceObject, TRUE, TRUE);
9236         }
9237     }
9238 
9239     *DeviceObject = deviceObject;
9240 
9241     return status;
9242 } // end ClassCreateDeviceObject()
9243 
9244 /*++////////////////////////////////////////////////////////////////////////////
9245 
9246 ClassClaimDevice()
9247 
9248 Routine Description:
9249 
9250     This function claims a device in the port driver.  The port driver object
9251     is updated with the correct driver object if the device is successfully
9252     claimed.
9253 
9254 Arguments:
9255 
9256     LowerDeviceObject - Supplies the base port device object.
9257 
9258     Release - Indicates the logical unit should be released rather than claimed.
9259 
9260 Return Value:
9261 
9262     Returns a status indicating success or failure of the operation.
9263 
9264 --*/
9265 _IRQL_requires_max_(PASSIVE_LEVEL)
9266 NTSTATUS
9267 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9268 ClassClaimDevice(
9269     _In_ PDEVICE_OBJECT LowerDeviceObject,
9270     _In_ BOOLEAN Release
9271     )
9272 {
9273     IO_STATUS_BLOCK    ioStatus;
9274     PIRP               irp;
9275     PIO_STACK_LOCATION irpStack;
9276     KEVENT             event;
9277     NTSTATUS           status;
9278     SCSI_REQUEST_BLOCK srb = {0};
9279 
9280     PAGED_CODE();
9281 
9282     //
9283     // WORK ITEM - MPIO related. Need to think about how to handle.
9284     //
9285 
9286     srb.Length = sizeof(SCSI_REQUEST_BLOCK);
9287 
9288     srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
9289         SRB_FUNCTION_CLAIM_DEVICE;
9290 
9291     //
9292     // Set the event object to the unsignaled state.
9293     // It will be used to signal request completion
9294     //
9295 
9296     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
9297 
9298     //
9299     // Build synchronous request with no transfer.
9300     //
9301 
9302     irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
9303                                         LowerDeviceObject,
9304                                         NULL,
9305                                         0,
9306                                         NULL,
9307                                         0,
9308                                         TRUE,
9309                                         &event,
9310                                         &ioStatus);
9311 
9312     if (irp == NULL) {
9313         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,  "ClassClaimDevice: Can't allocate Irp\n"));
9314         return STATUS_INSUFFICIENT_RESOURCES;
9315     }
9316 
9317     irpStack = IoGetNextIrpStackLocation(irp);
9318 
9319     //
9320     // Save SRB address in next stack for port driver.
9321     //
9322 
9323     irpStack->Parameters.Scsi.Srb = &srb;
9324 
9325     //
9326     // Set up IRP Address.
9327     //
9328 
9329     srb.OriginalRequest = irp;
9330 
9331     //
9332     // Call the port driver with the request and wait for it to complete.
9333     //
9334 
9335     status = IoCallDriver(LowerDeviceObject, irp);
9336     if (status == STATUS_PENDING) {
9337 
9338         (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
9339         status = ioStatus.Status;
9340     }
9341 
9342     //
9343     // If this is a release request, then just decrement the reference count
9344     // and return.  The status does not matter.
9345     //
9346 
9347     if (Release) {
9348 
9349         // ObDereferenceObject(LowerDeviceObject);
9350         return STATUS_SUCCESS;
9351     }
9352 
9353     if (!NT_SUCCESS(status)) {
9354         return status;
9355     }
9356 
9357     NT_ASSERT(srb.DataBuffer != NULL);
9358     NT_ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
9359 
9360     return status;
9361 } // end ClassClaimDevice()
9362 
9363 /*++////////////////////////////////////////////////////////////////////////////
9364 
9365 ClassInternalIoControl()
9366 
9367 Routine Description:
9368 
9369     This routine passes internal device controls to the port driver.
9370     Internal device controls are used by higher level drivers both for ioctls
9371     and to pass through scsi requests.
9372 
9373     If the IoControlCode does not match any of the handled ioctls and is
9374     a valid system address then the request will be treated as an SRB and
9375     passed down to the lower driver.  If the IoControlCode is not a valid
9376     system address the ioctl will be failed.
9377 
9378     Callers must therefore be extremely cautious to pass correct, initialized
9379     values to this function.
9380 
9381 Arguments:
9382 
9383     DeviceObject - Supplies a pointer to the device object for this request.
9384 
9385     Irp - Supplies the Irp making the request.
9386 
9387 Return Value:
9388 
9389    Returns back a STATUS_PENDING or a completion status.
9390 
9391 --*/
9392 NTSTATUS
9393 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9394 ClassInternalIoControl(
9395     IN PDEVICE_OBJECT DeviceObject,
9396     IN PIRP Irp
9397     )
9398 {
9399     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
9400 
9401     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
9402     PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
9403 
9404     ULONG isRemoved;
9405 
9406     PSCSI_REQUEST_BLOCK srb;
9407 
9408     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
9409     _Analysis_assume_(isRemoved);
9410     if(isRemoved) {
9411 
9412         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
9413 
9414         ClassReleaseRemoveLock(DeviceObject, Irp);
9415 
9416         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
9417 
9418         return STATUS_DEVICE_DOES_NOT_EXIST;
9419     }
9420 
9421     //
9422     // Get a pointer to the SRB.
9423     //
9424 
9425     srb = irpStack->Parameters.Scsi.Srb;
9426 
9427     //
9428     // Set the parameters in the next stack location.
9429     //
9430 
9431     if(commonExtension->IsFdo) {
9432         nextStack->Parameters.Scsi.Srb = srb;
9433         nextStack->MajorFunction = IRP_MJ_SCSI;
9434         nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
9435 
9436     } else {
9437 
9438         IoCopyCurrentIrpStackLocationToNext(Irp);
9439     }
9440 
9441     ClassReleaseRemoveLock(DeviceObject, Irp);
9442 
9443     return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
9444 } // end ClassInternalIoControl()
9445 
9446 /*++////////////////////////////////////////////////////////////////////////////
9447 
9448 ClassQueryTimeOutRegistryValue()
9449 
9450 Routine Description:
9451 
9452     This routine determines whether a reg key for a user-specified timeout
9453     value exists.  This should be called at initialization time.
9454 
9455 Arguments:
9456 
9457     DeviceObject - Pointer to the device object we are retrieving the timeout
9458                    value for
9459 
9460 Return Value:
9461 
9462     None, but it sets a new default timeout for a class of devices.
9463 
9464 --*/
9465 _IRQL_requires_max_(PASSIVE_LEVEL)
9466 ULONG
9467 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9468 ClassQueryTimeOutRegistryValue(
9469     _In_ PDEVICE_OBJECT DeviceObject
9470     )
9471 {
9472     //
9473     // Find the appropriate reg. key
9474     //
9475 
9476 #ifdef _MSC_VER
9477 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
9478 #endif
9479     PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
9480 
9481     PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
9482 
9483     PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
9484     PWSTR path;
9485     NTSTATUS status;
9486     LONG     timeOut = 0;
9487     ULONG    zero = 0;
9488     ULONG    size;
9489 
9490     PAGED_CODE();
9491 
9492     if (!registryPath) {
9493         return 0;
9494     }
9495 
9496     parameters = ExAllocatePoolWithTag(NonPagedPoolNx,
9497                                 sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
9498                                 '1BcS');
9499 
9500     if (!parameters) {
9501         return 0;
9502     }
9503 
9504     size = registryPath->MaximumLength + sizeof(WCHAR);
9505     path = ExAllocatePoolWithTag(NonPagedPoolNx, size, '2BcS');
9506 
9507     if (!path) {
9508         FREE_POOL(parameters);
9509         return 0;
9510     }
9511 
9512     RtlZeroMemory(path,size);
9513     RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
9514 
9515 
9516     //
9517     // Check for the Timeout value.
9518     //
9519 
9520     RtlZeroMemory(parameters,
9521                   (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
9522 
9523     parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
9524     parameters[0].Name          = L"TimeOutValue";
9525     parameters[0].EntryContext  = &timeOut;
9526     parameters[0].DefaultType   = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD;
9527     parameters[0].DefaultData   = &zero;
9528     parameters[0].DefaultLength = sizeof(ULONG);
9529 
9530     status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
9531                                     path,
9532                                     parameters,
9533                                     NULL,
9534                                     NULL);
9535 
9536     if (!(NT_SUCCESS(status))) {
9537         timeOut = 0;
9538     }
9539 
9540     FREE_POOL(parameters);
9541     FREE_POOL(path);
9542 
9543     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
9544                 "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
9545                 timeOut));
9546 
9547 
9548     return timeOut;
9549 
9550 } // end ClassQueryTimeOutRegistryValue()
9551 
9552 
9553 /*++////////////////////////////////////////////////////////////////////////////
9554 
9555 ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
9556 
9557 Routine Description:
9558 
9559     This routine executes when the port driver has completed a check verify
9560     ioctl.  It will set the status of the master Irp, copy the media change
9561     count and complete the request.
9562 
9563 Arguments:
9564 
9565     Fdo - Supplies the functional device object which represents the logical unit.
9566 
9567     Irp - Supplies the Irp which has completed.
9568 
9569     Context - NULL
9570 
9571 Return Value:
9572 
9573     NT status
9574 
9575 --*/
9576 NTSTATUS
9577 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9578 ClassCheckVerifyComplete(
9579     IN PDEVICE_OBJECT Fdo,
9580     IN PIRP Irp,
9581     IN PVOID Context
9582     )
9583 {
9584     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
9585     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
9586 
9587     PIRP originalIrp;
9588 
9589     UNREFERENCED_PARAMETER(Context);
9590 
9591     ASSERT_FDO(Fdo);
9592 
9593     originalIrp = irpStack->Parameters.Others.Argument1;
9594 
9595     //
9596     // Copy the media change count and status
9597     //
9598 
9599     *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
9600         fdoExtension->MediaChangeCount;
9601 
9602     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClassCheckVerifyComplete - Media change count for"
9603                    "device %d is %lx - saved as %lx\n",
9604                 fdoExtension->DeviceNumber,
9605                 fdoExtension->MediaChangeCount,
9606                 *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
9607 
9608     originalIrp->IoStatus.Status = Irp->IoStatus.Status;
9609     originalIrp->IoStatus.Information = sizeof(ULONG);
9610 
9611     ClassReleaseRemoveLock(Fdo, originalIrp);
9612     ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
9613 
9614     IoFreeIrp(Irp);
9615 
9616     return STATUS_MORE_PROCESSING_REQUIRED;
9617 
9618 } // end ClassCheckVerifyComplete()
9619 
9620 
9621 /*++////////////////////////////////////////////////////////////////////////////
9622 
9623 ClassGetDescriptor()
9624 
9625 Routine Description:
9626 
9627     This routine will perform a query for the specified property id and will
9628     allocate a non-paged buffer to store the data in.  It is the responsibility
9629     of the caller to ensure that this buffer is freed.
9630 
9631     This routine must be run at IRQL_PASSIVE_LEVEL
9632 
9633 Arguments:
9634 
9635     DeviceObject - the device to query
9636     DeviceInfo - a location to store a pointer to the buffer we allocate
9637 
9638 Return Value:
9639 
9640     status
9641     if status is unsuccessful *DeviceInfo will be set to NULL, else the
9642     buffer allocated on behalf of the caller.
9643 
9644 --*/
9645 _IRQL_requires_max_(PASSIVE_LEVEL)
9646 NTSTATUS
9647 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9648 ClassGetDescriptor(
9649     _In_ PDEVICE_OBJECT DeviceObject,
9650     _In_ PSTORAGE_PROPERTY_ID PropertyId,
9651     _Outptr_ PVOID *Descriptor
9652     )
9653 {
9654     STORAGE_PROPERTY_QUERY query = {0};
9655     IO_STATUS_BLOCK ioStatus;
9656 
9657     PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
9658     ULONG length;
9659 
9660     PAGED_CODE();
9661 
9662     //
9663     // Set the passed-in descriptor pointer to NULL as default
9664     //
9665 
9666     *Descriptor = NULL;
9667 
9668     query.PropertyId = *PropertyId;
9669     query.QueryType = PropertyStandardQuery;
9670 
9671     //
9672     // On the first pass we just want to get the first few
9673     // bytes of the descriptor so we can read it's size
9674     //
9675 
9676     descriptor = (PVOID)&query;
9677 
9678     NT_ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
9679 
9680     ClassSendDeviceIoControlSynchronous(
9681         IOCTL_STORAGE_QUERY_PROPERTY,
9682         DeviceObject,
9683         &query,
9684         sizeof(STORAGE_PROPERTY_QUERY),
9685         sizeof(ULONG) * 2,
9686         FALSE,
9687         &ioStatus
9688         );
9689 
9690     if(!NT_SUCCESS(ioStatus.Status)) {
9691 
9692         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "ClassGetDescriptor: error %lx trying to "
9693                        "query properties #1\n", ioStatus.Status));
9694         return ioStatus.Status;
9695     }
9696 
9697     if (descriptor->Size == 0) {
9698 
9699         //
9700         // This DebugPrint is to help third-party driver writers
9701         //
9702 
9703         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "ClassGetDescriptor: size returned was zero?! (status "
9704                     "%x\n", ioStatus.Status));
9705         return STATUS_UNSUCCESSFUL;
9706 
9707     }
9708 
9709     //
9710     // This time we know how much data there is so we can
9711     // allocate a buffer of the correct size
9712     //
9713 
9714     length = descriptor->Size;
9715     NT_ASSERT(length >= sizeof(STORAGE_PROPERTY_QUERY));
9716     length = max(length, sizeof(STORAGE_PROPERTY_QUERY));
9717 
9718     descriptor = ExAllocatePoolWithTag(NonPagedPoolNx, length, '4BcS');
9719 
9720     if(descriptor == NULL) {
9721 
9722         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "ClassGetDescriptor: unable to memory for descriptor "
9723                     "(%d bytes)\n", length));
9724         return STATUS_INSUFFICIENT_RESOURCES;
9725     }
9726 
9727     //
9728     // setup the query again, as it was overwritten above
9729     //
9730 
9731     RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
9732     query.PropertyId = *PropertyId;
9733     query.QueryType = PropertyStandardQuery;
9734 
9735     //
9736     // copy the input to the new outputbuffer
9737     //
9738 
9739     RtlZeroMemory(descriptor, length);
9740 
9741     RtlCopyMemory(descriptor,
9742                   &query,
9743                   sizeof(STORAGE_PROPERTY_QUERY)
9744                   );
9745 
9746     ClassSendDeviceIoControlSynchronous(
9747         IOCTL_STORAGE_QUERY_PROPERTY,
9748         DeviceObject,
9749         descriptor,
9750         sizeof(STORAGE_PROPERTY_QUERY),
9751         length,
9752         FALSE,
9753         &ioStatus
9754         );
9755 
9756     if(!NT_SUCCESS(ioStatus.Status)) {
9757 
9758         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,  "ClassGetDescriptor: error %lx trying to "
9759                        "query properties #1\n", ioStatus.Status));
9760         FREE_POOL(descriptor);
9761         return ioStatus.Status;
9762     }
9763 
9764     //
9765     // return the memory we've allocated to the caller
9766     //
9767 
9768     *Descriptor = descriptor;
9769     return ioStatus.Status;
9770 } // end ClassGetDescriptor()
9771 
9772 
9773 /*++////////////////////////////////////////////////////////////////////////////
9774 
9775 ClassSignalCompletion()
9776 
9777 Routine Description:
9778 
9779     This completion routine will signal the event given as context and then
9780     return STATUS_MORE_PROCESSING_REQUIRED to stop event completion.  It is
9781     the responsibility of the routine waiting on the event to complete the
9782     request and free the event.
9783 
9784 Arguments:
9785 
9786     DeviceObject - a pointer to the device object
9787 
9788     Irp - a pointer to the irp
9789 
9790     Event - a pointer to the event to signal
9791 
9792 Return Value:
9793 
9794     STATUS_MORE_PROCESSING_REQUIRED
9795 
9796 --*/
9797 NTSTATUS
9798 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9799 ClassSignalCompletion(
9800     IN PDEVICE_OBJECT DeviceObject,
9801     IN PIRP Irp,
9802     IN PVOID Context
9803     )
9804 {
9805     PKEVENT Event = (PKEVENT)Context;
9806 
9807     UNREFERENCED_PARAMETER(DeviceObject);
9808     UNREFERENCED_PARAMETER(Irp);
9809 
9810     if (Context == NULL) {
9811         NT_ASSERT(Context != NULL);
9812         return STATUS_INVALID_PARAMETER;
9813     }
9814 
9815     KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
9816 
9817     return STATUS_MORE_PROCESSING_REQUIRED;
9818 } // end ClassSignalCompletion()
9819 
9820 /*++////////////////////////////////////////////////////////////////////////////
9821 
9822 ClassPnpQueryFdoRelations()
9823 
9824 Routine Description:
9825 
9826     This routine will call the driver's enumeration routine to update the
9827     list of PDO's.  It will then build a response to the
9828     IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
9829     the irp.
9830 
9831 Arguments:
9832 
9833     Fdo - a pointer to the functional device object we are enumerating
9834 
9835     Irp - a pointer to the enumeration request
9836 
9837 Return Value:
9838 
9839     status
9840 
9841 --*/
9842 NTSTATUS
9843 ClassPnpQueryFdoRelations(
9844     IN PDEVICE_OBJECT Fdo,
9845     IN PIRP Irp
9846     )
9847 {
9848     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
9849 #ifdef _MSC_VER
9850 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
9851 #endif
9852     PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
9853 
9854     PAGED_CODE();
9855 
9856     _Analysis_assume_(driverExtension != NULL);
9857 
9858     //
9859     // If there's already an enumeration in progress then don't start another
9860     // one.
9861     //
9862 
9863     if(InterlockedIncrement((volatile LONG *)&(fdoExtension->EnumerationInterlock)) == 1) {
9864         driverExtension->InitData.ClassEnumerateDevice(Fdo);
9865     }
9866 
9867     Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
9868                                 Fdo,
9869                                 BusRelations,
9870                                 (PDEVICE_RELATIONS *) &Irp->IoStatus.Information);
9871     InterlockedDecrement((volatile LONG *)&(fdoExtension->EnumerationInterlock));
9872 
9873     return Irp->IoStatus.Status;
9874 } // end ClassPnpQueryFdoRelations()
9875 
9876 /*++////////////////////////////////////////////////////////////////////////////
9877 
9878 ClassMarkChildrenMissing()
9879 
9880 Routine Description:
9881 
9882     This routine will call ClassMarkChildMissing() for all children.
9883     It acquires the ChildLock before calling ClassMarkChildMissing().
9884 
9885 Arguments:
9886 
9887     Fdo - the "bus's" device object, such as the disk FDO for non-removable
9888         disks with multiple partitions.
9889 
9890 Return Value:
9891 
9892     None
9893 
9894 --*/
9895 _IRQL_requires_max_(PASSIVE_LEVEL)
9896 VOID
9897 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9898 ClassMarkChildrenMissing(
9899     _In_ PFUNCTIONAL_DEVICE_EXTENSION Fdo
9900     )
9901 {
9902     PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
9903     PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
9904 
9905     PAGED_CODE();
9906 
9907     ClassAcquireChildLock(Fdo);
9908 
9909     while (nextChild){
9910         PPHYSICAL_DEVICE_EXTENSION tmpChild;
9911 
9912         /*
9913          *  ClassMarkChildMissing will also dequeue the child extension.
9914          *  So get the next pointer before calling ClassMarkChildMissing.
9915          */
9916         tmpChild = nextChild;
9917         nextChild = tmpChild->CommonExtension.ChildList;
9918         ClassMarkChildMissing(tmpChild, FALSE);
9919     }
9920     ClassReleaseChildLock(Fdo);
9921     return;
9922 } // end ClassMarkChildrenMissing()
9923 
9924 /*++////////////////////////////////////////////////////////////////////////////
9925 
9926 ClassMarkChildMissing()
9927 
9928 Routine Description:
9929 
9930     This routine will make an active child "missing."  If the device has never
9931     been enumerated then it will be deleted on the spot.  If the device has
9932     not been enumerated then it will be marked as missing so that we can
9933     not report it in the next device enumeration.
9934 
9935 Arguments:
9936 
9937     Child - the child device to be marked as missing.
9938 
9939     AcquireChildLock - TRUE if the child lock should be acquired before removing
9940                        the missing child.  FALSE if the child lock is already
9941                        acquired by this thread.
9942 
9943 Return Value:
9944 
9945     returns whether or not the child device object has previously been reported
9946     to PNP.
9947 
9948 --*/
9949 _IRQL_requires_max_(PASSIVE_LEVEL)
9950 BOOLEAN
9951 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
9952 ClassMarkChildMissing(
9953     _In_ PPHYSICAL_DEVICE_EXTENSION Child,
9954     _In_ BOOLEAN AcquireChildLock
9955     )
9956 {
9957     BOOLEAN returnValue = Child->IsEnumerated;
9958 
9959     PAGED_CODE();
9960     ASSERT_PDO(Child->DeviceObject);
9961 
9962     Child->IsMissing = TRUE;
9963 
9964     //
9965     // Make sure this child is not in the active list.
9966     //
9967 
9968     ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
9969                      Child,
9970                      AcquireChildLock);
9971 
9972     if(Child->IsEnumerated == FALSE) {
9973         PCOMMON_DEVICE_EXTENSION commonExtension = Child->DeviceObject->DeviceExtension;
9974         commonExtension->IsRemoved = REMOVE_PENDING;
9975         ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
9976     }
9977 
9978     return returnValue;
9979 } // end ClassMarkChildMissing()
9980 
9981 /*++////////////////////////////////////////////////////////////////////////////
9982 
9983 ClassRetrieveDeviceRelations()
9984 
9985 Routine Description:
9986 
9987     This routine will allocate a buffer to hold the specified list of
9988     relations.  It will then fill in the list with referenced device pointers
9989     and will return the request.
9990 
9991 Arguments:
9992 
9993     Fdo - pointer to the FDO being queried
9994 
9995     RelationType - what type of relations are being queried
9996 
9997     DeviceRelations - a location to store a pointer to the response
9998 
9999 Return Value:
10000 
10001     status
10002 
10003 --*/
10004 NTSTATUS
10005 ClassRetrieveDeviceRelations(
10006     IN PDEVICE_OBJECT Fdo,
10007     IN DEVICE_RELATION_TYPE RelationType,
10008     OUT PDEVICE_RELATIONS *DeviceRelations
10009     )
10010 {
10011     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
10012 
10013     ULONG count = 0;
10014     ULONG i;
10015 
10016     PPHYSICAL_DEVICE_EXTENSION nextChild;
10017 
10018     ULONG relationsSize;
10019     PDEVICE_RELATIONS deviceRelations = NULL;
10020     PDEVICE_RELATIONS oldRelations = *DeviceRelations;
10021 
10022     NTSTATUS status;
10023 
10024     UNREFERENCED_PARAMETER(RelationType);
10025 
10026     PAGED_CODE();
10027 
10028     ClassAcquireChildLock(fdoExtension);
10029 
10030     nextChild = fdoExtension->CommonExtension.ChildList;
10031 
10032     //
10033     // Count the number of PDO's attached to this disk
10034     //
10035 
10036     while (nextChild != NULL) {
10037         PCOMMON_DEVICE_EXTENSION commonExtension;
10038 
10039         commonExtension = &(nextChild->CommonExtension);
10040 
10041         NT_ASSERTMSG("ClassPnp internal error: missing child on active list\n",
10042                   (nextChild->IsMissing == FALSE));
10043 
10044         nextChild = commonExtension->ChildList;
10045 
10046         count++;
10047     };
10048 
10049     //
10050     // If relations already exist in the QDR, adjust the current count
10051     // to include the previous list.
10052     //
10053 
10054     if (oldRelations) {
10055         count += oldRelations->Count;
10056     }
10057 
10058     relationsSize = (sizeof(DEVICE_RELATIONS) +
10059                      (count * sizeof(PDEVICE_OBJECT)));
10060 
10061     deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
10062 
10063     if (deviceRelations == NULL) {
10064 
10065         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_ENUM,  "ClassRetrieveDeviceRelations: unable to allocate "
10066                        "%d bytes for device relations\n", relationsSize));
10067 
10068         ClassReleaseChildLock(fdoExtension);
10069 
10070         return STATUS_INSUFFICIENT_RESOURCES;
10071     }
10072 
10073     RtlZeroMemory(deviceRelations, relationsSize);
10074 
10075     if (oldRelations) {
10076 
10077         //
10078         // Copy the old relations to the new list and free the old list.
10079         //
10080 
10081         for (i = 0; i < oldRelations->Count; i++) {
10082             deviceRelations->Objects[i] = oldRelations->Objects[i];
10083         }
10084 
10085         FREE_POOL(oldRelations);
10086     }
10087 
10088     nextChild = fdoExtension->CommonExtension.ChildList;
10089     i = count;
10090 
10091     while (nextChild != NULL) {
10092         PCOMMON_DEVICE_EXTENSION commonExtension;
10093 
10094         commonExtension = &(nextChild->CommonExtension);
10095 
10096         NT_ASSERTMSG("ClassPnp internal error: missing child on active list\n",
10097                   (nextChild->IsMissing == FALSE));
10098 
10099     _Analysis_assume_(i >= 1);
10100     deviceRelations->Objects[--i] = nextChild->DeviceObject;
10101 
10102         status = ObReferenceObjectByPointer(
10103                     nextChild->DeviceObject,
10104                     0,
10105                     NULL,
10106                     KernelMode);
10107         if (!NT_SUCCESS(status)) {
10108             NT_ASSERT(!"Error referencing child device by pointer");
10109             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM,
10110                         "ClassRetrieveDeviceRelations: Error referencing child "
10111                         "device %p by pointer\n", nextChild->DeviceObject));
10112 
10113         }
10114         nextChild->IsEnumerated = TRUE;
10115         nextChild = commonExtension->ChildList;
10116     }
10117 
10118     NT_ASSERTMSG("Child list has changed: ", i == 0);
10119 
10120     deviceRelations->Count = count;
10121     *DeviceRelations = deviceRelations;
10122 
10123     ClassReleaseChildLock(fdoExtension);
10124     return STATUS_SUCCESS;
10125 } // end ClassRetrieveDeviceRelations()
10126 
10127 /*++////////////////////////////////////////////////////////////////////////////
10128 
10129 ClassGetPdoId()
10130 
10131 Routine Description:
10132 
10133     This routine will call into the driver to retrieve a copy of one of it's
10134     id strings.
10135 
10136 Arguments:
10137 
10138     Pdo - a pointer to the pdo being queried
10139 
10140     IdType - which type of id string is being queried
10141 
10142     IdString - an allocated unicode string structure which the driver
10143                can fill in.
10144 
10145 Return Value:
10146 
10147     status
10148 
10149 --*/
10150 NTSTATUS
10151 ClassGetPdoId(
10152     IN PDEVICE_OBJECT Pdo,
10153     IN BUS_QUERY_ID_TYPE IdType,
10154     IN PUNICODE_STRING IdString
10155     )
10156 {
10157 #ifdef _MSC_VER
10158 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
10159 #endif
10160     PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
10161 
10162     _Analysis_assume_(driverExtension != NULL);
10163 
10164     ASSERT_PDO(Pdo);
10165     NT_ASSERT(driverExtension->InitData.ClassQueryId);
10166 
10167     PAGED_CODE();
10168 
10169     return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
10170 } // end ClassGetPdoId()
10171 
10172 /*++////////////////////////////////////////////////////////////////////////////
10173 
10174 ClassQueryPnpCapabilities()
10175 
10176 Routine Description:
10177 
10178     This routine will call into the class driver to retrieve it's pnp
10179     capabilities.
10180 
10181 Arguments:
10182 
10183     PhysicalDeviceObject - The physical device object to retrieve properties
10184                            for.
10185 
10186 Return Value:
10187 
10188     status
10189 
10190 --*/
10191 NTSTATUS
10192 ClassQueryPnpCapabilities(
10193     IN PDEVICE_OBJECT DeviceObject,
10194     IN PDEVICE_CAPABILITIES Capabilities
10195     )
10196 {
10197     PCLASS_DRIVER_EXTENSION driverExtension =
10198         ClassGetDriverExtension(DeviceObject->DriverObject);
10199     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
10200 
10201     PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
10202 
10203     PAGED_CODE();
10204 
10205     NT_ASSERT(DeviceObject);
10206     NT_ASSERT(Capabilities);
10207 
10208     if(commonExtension->IsFdo) {
10209         queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
10210     } else {
10211         queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
10212     }
10213 
10214     if(queryRoutine) {
10215         return queryRoutine(DeviceObject,
10216                             Capabilities);
10217     } else {
10218         return STATUS_NOT_IMPLEMENTED;
10219     }
10220 } // end ClassQueryPnpCapabilities()
10221 
10222 /*++////////////////////////////////////////////////////////////////////////////
10223 
10224 ClassInvalidateBusRelations()
10225 
10226 Routine Description:
10227 
10228     This routine re-enumerates the devices on the "bus".  It will call into
10229     the driver's ClassEnumerate routine to update the device objects
10230     immediately.  It will then schedule a bus re-enumeration for pnp by calling
10231     IoInvalidateDeviceRelations.
10232 
10233 Arguments:
10234 
10235     Fdo - a pointer to the functional device object for this bus
10236 
10237 Return Value:
10238 
10239     none
10240 
10241 --*/
10242 _IRQL_requires_max_(PASSIVE_LEVEL)
10243 VOID
10244 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10245 ClassInvalidateBusRelations(
10246     _In_ PDEVICE_OBJECT Fdo
10247     )
10248 {
10249     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
10250 #ifdef _MSC_VER
10251 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
10252 #endif
10253     PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
10254 
10255     NTSTATUS status = STATUS_SUCCESS;
10256 
10257     PAGED_CODE();
10258 
10259     _Analysis_assume_(driverExtension != NULL);
10260 
10261     ASSERT_FDO(Fdo);
10262     NT_ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
10263 
10264     if(InterlockedIncrement((volatile LONG *)&(fdoExtension->EnumerationInterlock)) == 1) {
10265         status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
10266     }
10267     InterlockedDecrement((volatile LONG *)&(fdoExtension->EnumerationInterlock));
10268 
10269     if(!NT_SUCCESS(status)) {
10270 
10271         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_ENUM,  "ClassInvalidateBusRelations: EnumerateDevice routine "
10272                        "returned %lx\n", status));
10273     }
10274 
10275     IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
10276 
10277     return;
10278 } // end ClassInvalidateBusRelations()
10279 
10280 /*++////////////////////////////////////////////////////////////////////////////
10281 
10282 ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
10283 
10284 Routine Description:
10285 
10286     This routine is called to handle the "removal" of a device.  It will
10287     forward the request downwards if necesssary, call into the driver
10288     to release any necessary resources (memory, events, etc) and then
10289     will delete the device object.
10290 
10291 Arguments:
10292 
10293     DeviceObject - a pointer to the device object being removed
10294 
10295     RemoveType - indicates what type of remove this is (regular or surprise).
10296 
10297 Return Value:
10298 
10299     status
10300 
10301 --*/
10302 _IRQL_requires_max_(PASSIVE_LEVEL)
10303 NTSTATUS
10304 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10305 ClassRemoveDevice(
10306     _In_ PDEVICE_OBJECT DeviceObject,
10307     _In_ UCHAR RemoveType
10308     )
10309 {
10310 #ifdef _MSC_VER
10311 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
10312 #endif
10313     PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
10314     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
10315     PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
10316     BOOLEAN proceedWithRemove = TRUE;
10317     NTSTATUS status;
10318     PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
10319 
10320 
10321     PAGED_CODE();
10322 
10323     _Analysis_assume_(driverExtension != NULL);
10324 
10325     /*
10326      *  Deregister from WMI.
10327      */
10328     if (commonExtension->IsFdo ||
10329         driverExtension->InitData.PdoData.ClassWmiInfo.GuidRegInfo) {
10330         status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
10331         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
10332     }
10333 
10334     /*
10335      *  If we exposed a "shingle" (a named device interface openable by CreateFile)
10336      *  then delete it now.
10337      */
10338     if (commonExtension->MountedDeviceInterfaceName.Buffer){
10339         (VOID)IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
10340         RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
10341         RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
10342     }
10343 
10344     //
10345     // If this is a surprise removal we leave the device around - which means
10346     // we don't have to (or want to) drop the remove lock and wait for pending
10347     // requests to complete.
10348     //
10349 
10350     if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10351 
10352         //
10353         // Release the lock we acquired when the device object was created.
10354         //
10355 
10356         ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
10357 
10358         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP,  "ClasspRemoveDevice - Reference count is now %d\n",
10359                     commonExtension->RemoveLock));
10360 
10361         //
10362         // The RemoveLockRundown allows fast protection of the device object
10363         // structure that is torn down by a single thread. While in the rundown process (the call to
10364         // ExWaitForRundownProtectionReleaseCacheAware returns), the rundown object becomes
10365         // invalid and the subsequent calls to ExAcquireRundownProtectionCacheAware will return FALSE.
10366         // ExReInitializeRundownProtectionCacheAware needs to be called to re-initialize the
10367         // RemoveLockRundown protection.
10368         //
10369 
10370         removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
10371             ((PCHAR)commonExtension->PrivateCommonData +
10372              sizeof(CLASS_PRIVATE_COMMON_DATA));
10373         ExWaitForRundownProtectionReleaseCacheAware(removeLockRundown);
10374 
10375         KeSetEvent(&commonExtension->RemoveEvent,
10376                    IO_NO_INCREMENT,
10377                    FALSE);
10378 
10379         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP,  "ClasspRemoveDevice - removing device %p\n", DeviceObject));
10380 
10381         if (commonExtension->IsFdo) {
10382 
10383             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP,  "ClasspRemoveDevice - FDO %p has received a "
10384                            "remove request.\n", DeviceObject));
10385 
10386         } else {
10387             PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
10388 
10389             if (pdoExtension->IsMissing) {
10390                 /*
10391                  *  The child partition PDO is missing, so we are going to go ahead
10392                  *  and delete it for the remove.
10393                  */
10394                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
10395             } else {
10396                 /*
10397                  *  We got a remove for a child partition PDO which is not actually missing.
10398                  *  So we will NOT actually delete it.
10399                  */
10400                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
10401 
10402                 ExReInitializeRundownProtectionCacheAware(removeLockRundown);
10403 
10404                 //
10405                 // Reacquire the remove lock for the next time this comes around.
10406                 //
10407 
10408                 ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
10409 
10410                 //
10411                 // the device wasn't missing so it's not really been removed.
10412                 //
10413 
10414                 commonExtension->IsRemoved = NO_REMOVE;
10415 
10416                 IoInvalidateDeviceRelations(
10417                     commonExtension->PartitionZeroExtension->LowerPdo,
10418                     BusRelations);
10419 
10420                 proceedWithRemove = FALSE;
10421             }
10422         }
10423     }
10424 
10425 
10426     if (proceedWithRemove) {
10427 
10428         /*
10429          *  Call the class driver's remove handler.
10430          *  All this is supposed to do is clean up its data and device interfaces.
10431          */
10432         NT_ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
10433         status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
10434         NT_ASSERT(NT_SUCCESS(status));
10435         status = STATUS_SUCCESS;
10436         UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
10437 
10438         if (commonExtension->IsFdo) {
10439             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
10440 
10441             ClasspDisableTimer(fdoExtension);
10442 
10443             if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10444 
10445                 PPHYSICAL_DEVICE_EXTENSION child;
10446 
10447                 //
10448                 // If this FDO is idle power managed, remove it from the
10449                 // list of idle power managed FDOs.
10450                 //
10451                 if (fdoExtension->FunctionSupportInfo &&
10452                     fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled) {
10453                     PIDLE_POWER_FDO_LIST_ENTRY fdoEntry;
10454 
10455                     KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
10456                     fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)IdlePowerFDOList.Flink;
10457                     while ((PLIST_ENTRY)fdoEntry != &IdlePowerFDOList) {
10458                         PIDLE_POWER_FDO_LIST_ENTRY nextEntry = (PIDLE_POWER_FDO_LIST_ENTRY)fdoEntry->ListEntry.Flink;
10459                         if (fdoEntry->Fdo == DeviceObject) {
10460                             RemoveEntryList(&(fdoEntry->ListEntry));
10461                             ExFreePool(fdoEntry);
10462                             break;
10463                         }
10464                         fdoEntry = nextEntry;
10465                     }
10466                     KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
10467                 }
10468 
10469                 //
10470                 // Cleanup the media detection resources now that the class driver
10471                 // has stopped it's timer (if any) and we can be sure they won't
10472                 // call us to do detection again.
10473                 //
10474 
10475                 ClassCleanupMediaChangeDetection(fdoExtension);
10476 
10477                 //
10478                 // Cleanup any Failure Prediction stuff
10479                 //
10480                 FREE_POOL(fdoExtension->FailurePredictionInfo);
10481 
10482                 /*
10483                  *  Ordinarily all child PDOs will be removed by the time
10484                  *  that the parent gets the REMOVE_DEVICE.
10485                  *  However, if a child PDO has been created but has not
10486                  *  been announced in a QueryDeviceRelations, then it is
10487                  *  just a private data structure unknown to pnp, and we have
10488                  *  to delete it ourselves.
10489                  */
10490                 ClassAcquireChildLock(fdoExtension);
10491                 child = ClassRemoveChild(fdoExtension, NULL, FALSE);
10492                 while (child) {
10493                     PCOMMON_DEVICE_EXTENSION childCommonExtension = child->DeviceObject->DeviceExtension;
10494 
10495                     //
10496                     // Yank the pdo.  This routine will unlink the device from the
10497                     // pdo list so NextPdo will point to the next one when it's
10498                     // complete.
10499                     //
10500                     child->IsMissing = TRUE;
10501                     childCommonExtension->IsRemoved = REMOVE_PENDING;
10502                     ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
10503                     child = ClassRemoveChild(fdoExtension, NULL, FALSE);
10504                 }
10505                 ClassReleaseChildLock(fdoExtension);
10506             }
10507             else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
10508                 /*
10509                  *  This is a surprise-remove on the parent FDO.
10510                  *  We will mark the child PDOs as missing so that they
10511                  *  will actually get deleted when they get a REMOVE_DEVICE.
10512                  */
10513                 ClassMarkChildrenMissing(fdoExtension);
10514             }
10515 
10516             if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10517 
10518                 ClasspFreeReleaseRequest(DeviceObject);
10519 
10520                 //
10521                 // Free FDO-specific data structs
10522                 //
10523                 if (fdoExtension->PrivateFdoData) {
10524                     //
10525                     // Only remove the entry if the list has been initialized, or
10526                     // else we will access invalid memory.
10527                     //
10528                     PLIST_ENTRY allFdosListEntry = &fdoExtension->PrivateFdoData->AllFdosListEntry;
10529                     if (allFdosListEntry->Flink && allFdosListEntry->Blink) {
10530                         //
10531                         //  Remove the FDO from the static list.
10532                         //  Pnp is synchronized so this shouldn't need any synchronization.
10533                         //
10534                         RemoveEntryList(allFdosListEntry);
10535                     }
10536                     InitializeListHead(allFdosListEntry);
10537 
10538                     DestroyAllTransferPackets(DeviceObject);
10539 
10540                     //
10541                     // Delete the tick timer now.
10542                     //
10543                     ClasspDeleteTimer(fdoExtension);
10544 
10545 
10546                     FREE_POOL(fdoExtension->PrivateFdoData->PowerProcessIrp);
10547                     FREE_POOL(fdoExtension->PrivateFdoData->FreeTransferPacketsLists);
10548                     FREE_POOL(fdoExtension->PrivateFdoData);
10549                 }
10550 
10551 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
10552                 FREE_POOL(fdoExtension->AdditionalFdoData);
10553 #endif
10554 
10555                 if (commonExtension->DeviceName.Buffer) {
10556                     FREE_POOL(commonExtension->DeviceName.Buffer);
10557                     RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
10558                 }
10559 
10560 #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
10561                 if (fdoExtension->FunctionSupportInfo != NULL) {
10562                     FREE_POOL(fdoExtension->FunctionSupportInfo->HwFirmwareInfo);
10563                 }
10564 #endif
10565                 FREE_POOL(fdoExtension->FunctionSupportInfo);
10566 
10567                 FREE_POOL(fdoExtension->MiniportDescriptor);
10568 
10569                 FREE_POOL(fdoExtension->AdapterDescriptor);
10570 
10571                 FREE_POOL(fdoExtension->DeviceDescriptor);
10572 
10573                 //
10574                 // Detach our device object from the stack - there's no reason
10575                 // to hold off our cleanup any longer.
10576                 //
10577 
10578                 IoDetachDevice(lowerDeviceObject);
10579             }
10580         }
10581         else {
10582             /*
10583              *  This is a child partition PDO.
10584              *  We have already determined that it was previously marked
10585              *  as missing.  So if this is a REMOVE_DEVICE, we will actually
10586              *  delete it.
10587              */
10588             if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10589                 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
10590                 PPHYSICAL_DEVICE_EXTENSION pdoExtension = (PPHYSICAL_DEVICE_EXTENSION)commonExtension;
10591 
10592                 //
10593                 // See if this device is in the child list (if this was a suprise
10594                 // removal it might be) and remove it.
10595                 //
10596                 ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
10597             }
10598         }
10599 
10600         commonExtension->PartitionLength.QuadPart = 0;
10601 
10602         if (RemoveType == IRP_MN_REMOVE_DEVICE) {
10603 
10604             ClasspUninitializeRemoveTracking(DeviceObject);
10605 
10606             IoDeleteDevice(DeviceObject);
10607         }
10608     }
10609 
10610     return STATUS_SUCCESS;
10611 } // end ClassRemoveDevice()
10612 
10613 /*++////////////////////////////////////////////////////////////////////////////
10614 
10615 ClassGetDriverExtension()
10616 
10617 Routine Description:
10618 
10619     This routine will return the classpnp's driver extension.
10620 
10621 Arguments:
10622 
10623     DriverObject - the driver object for which to get classpnp's extension
10624 
10625 Return Value:
10626 
10627     Either NULL if none, or a pointer to the driver extension
10628 
10629 --*/
10630 __drv_aliasesMem
10631 _IRQL_requires_max_(DISPATCH_LEVEL)
10632 PCLASS_DRIVER_EXTENSION
10633 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10634 ClassGetDriverExtension(
10635     _In_ PDRIVER_OBJECT DriverObject
10636     )
10637 {
10638 #ifdef _MSC_VER
10639 #pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
10640 #endif
10641     return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
10642 } // end ClassGetDriverExtension()
10643 
10644 /*++////////////////////////////////////////////////////////////////////////////
10645 
10646 ClasspStartIo()
10647 
10648 Routine Description:
10649 
10650     This routine wraps the class driver's start io routine.  If the device
10651     is being removed it will complete any requests with
10652     STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
10653 
10654 Arguments:
10655 
10656 Return Value:
10657 
10658     none
10659 
10660 --*/
10661 VOID
10662 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10663 ClasspStartIo(
10664     IN PDEVICE_OBJECT DeviceObject,
10665     IN PIRP Irp
10666     )
10667 {
10668     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
10669 
10670     //
10671     // We're already holding the remove lock so just check the variable and
10672     // see what's going on.
10673     //
10674 
10675     if(commonExtension->IsRemoved) {
10676 
10677         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
10678 
10679 #ifdef _MSC_VER
10680 #pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
10681 #endif
10682         ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
10683 
10684         ClassReleaseRemoveLock(DeviceObject, Irp);
10685         ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
10686         IoStartNextPacket(DeviceObject, TRUE); // Some IO is cancellable
10687 
10688 #ifdef _MSC_VER
10689 #pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
10690 #endif
10691         ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
10692 
10693         return;
10694     }
10695 
10696     commonExtension->DriverExtension->InitData.ClassStartIo(
10697         DeviceObject,
10698         Irp);
10699 
10700     return;
10701 } // ClasspStartIo()
10702 
10703 /*++////////////////////////////////////////////////////////////////////////////
10704 
10705 ClassUpdateInformationInRegistry()
10706 
10707 Routine Description:
10708 
10709     This routine has knowledge about the layout of the device map information
10710     in the registry.  It will update this information to include a value
10711     entry specifying the dos device name that is assumed to get assigned
10712     to this NT device name.  For more information on this assigning of the
10713     dos device name look in the drive support routine in the hal that assigns
10714     all dos names.
10715 
10716     Since some versions of some device's firmware did not work and some
10717     vendors did not bother to follow the specification, the entire inquiry
10718     information must also be stored in the registry so than someone can
10719     figure out the firmware version.
10720 
10721 Arguments:
10722 
10723     DeviceObject - A pointer to the device object for the tape device.
10724 
10725 Return Value:
10726 
10727     None
10728 
10729 --*/
10730 _IRQL_requires_max_(PASSIVE_LEVEL)
10731 VOID
10732 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10733 ClassUpdateInformationInRegistry(
10734     _In_ PDEVICE_OBJECT   Fdo,
10735     _In_ PCHAR            DeviceName,
10736     _In_ ULONG            DeviceNumber,
10737     _In_reads_bytes_opt_(InquiryDataLength) PINQUIRYDATA InquiryData,
10738     _In_ ULONG            InquiryDataLength
10739     )
10740 {
10741     NTSTATUS          status;
10742     SCSI_ADDRESS      scsiAddress = {0};
10743     OBJECT_ATTRIBUTES objectAttributes = {0};
10744     STRING            string;
10745     UNICODE_STRING    unicodeName = {0};
10746     UNICODE_STRING    unicodeRegistryPath = {0};
10747     UNICODE_STRING    unicodeData = {0};
10748     HANDLE            targetKey;
10749     IO_STATUS_BLOCK   ioStatus;
10750     UCHAR buffer[256] = {0};
10751 
10752     PAGED_CODE();
10753 
10754     NT_ASSERT(DeviceName);
10755     targetKey = NULL;
10756 
10757     _SEH2_TRY {
10758 
10759         //
10760         // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
10761         //
10762 
10763         ClassSendDeviceIoControlSynchronous(
10764             IOCTL_SCSI_GET_ADDRESS,
10765             Fdo,
10766             &scsiAddress,
10767             0,
10768             sizeof(SCSI_ADDRESS),
10769             FALSE,
10770             &ioStatus
10771             );
10772 
10773         if (!NT_SUCCESS(ioStatus.Status)) {
10774 
10775             status = ioStatus.Status;
10776             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
10777                         "UpdateInformationInRegistry: Get Address failed %lx\n",
10778                         status));
10779             _SEH2_LEAVE;
10780 
10781         } else {
10782 
10783             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
10784                         "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
10785                         scsiAddress.PortNumber,
10786                         scsiAddress.PathId,
10787                         scsiAddress.TargetId,
10788                         scsiAddress.Lun));
10789 
10790         }
10791 
10792         status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer,
10793                 sizeof(buffer)-1,
10794                 "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
10795                 scsiAddress.PortNumber,
10796                 scsiAddress.PathId,
10797                 scsiAddress.TargetId,
10798                 scsiAddress.Lun);
10799 
10800         if (!NT_SUCCESS(status)) {
10801             _SEH2_LEAVE;
10802         }
10803 
10804         RtlInitString(&string, (PCSZ)buffer);
10805 
10806         status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
10807                                               &string,
10808                                               TRUE);
10809 
10810         if (!NT_SUCCESS(status)) {
10811             _SEH2_LEAVE;
10812         }
10813 
10814         //
10815         // Open the registry key for the scsi information for this
10816         // scsibus, target, lun.
10817         //
10818 
10819         InitializeObjectAttributes(&objectAttributes,
10820                                    &unicodeRegistryPath,
10821                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
10822                                    NULL,
10823                                    NULL);
10824 
10825         status = ZwOpenKey(&targetKey,
10826                            KEY_READ | KEY_WRITE,
10827                            &objectAttributes);
10828 
10829         if (!NT_SUCCESS(status)) {
10830             _SEH2_LEAVE;
10831         }
10832 
10833         //
10834         // Now construct and attempt to create the registry value
10835         // specifying the device name in the appropriate place in the
10836         // device map.
10837         //
10838 
10839         RtlInitUnicodeString(&unicodeName, L"DeviceName");
10840 
10841         status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer, sizeof(buffer)-1, "%s%d", DeviceName, DeviceNumber);
10842         if (!NT_SUCCESS(status)) {
10843             _SEH2_LEAVE;
10844         }
10845 
10846         RtlInitString(&string, (PCSZ)buffer);
10847         status = RtlAnsiStringToUnicodeString(&unicodeData,
10848                                               &string,
10849                                               TRUE);
10850         if (NT_SUCCESS(status)) {
10851             status = ZwSetValueKey(targetKey,
10852                                    &unicodeName,
10853                                    0,
10854                                    REG_SZ,
10855                                    unicodeData.Buffer,
10856                                    unicodeData.Length);
10857         }
10858 
10859         //
10860         // if they sent in data, update the registry
10861         //
10862 
10863         if (NT_SUCCESS(status) && InquiryDataLength) {
10864 
10865             NT_ASSERT(InquiryData);
10866 
10867             RtlInitUnicodeString(&unicodeName, L"InquiryData");
10868             status = ZwSetValueKey(targetKey,
10869                                    &unicodeName,
10870                                    0,
10871                                    REG_BINARY,
10872                                    InquiryData,
10873                                    InquiryDataLength);
10874         }
10875 
10876         // that's all, except to clean up.
10877 
10878     } _SEH2_FINALLY {
10879 
10880         if (!NT_SUCCESS(status)) {
10881             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
10882                         "Failure to update information in registry: %08x\n",
10883                         status
10884                         ));
10885         }
10886 
10887         if (unicodeData.Buffer) {
10888             RtlFreeUnicodeString(&unicodeData);
10889         }
10890         if (unicodeRegistryPath.Buffer) {
10891             RtlFreeUnicodeString(&unicodeRegistryPath);
10892         }
10893         if (targetKey) {
10894             ZwClose(targetKey);
10895         }
10896 
10897     } _SEH2_END;
10898 
10899 } // end ClassUpdateInformationInRegistry()
10900 
10901 /*++////////////////////////////////////////////////////////////////////////////
10902 
10903 ClasspSendSynchronousCompletion()
10904 
10905 Routine Description:
10906 
10907     This completion routine will set the user event in the irp after
10908     freeing the irp and the associated MDL (if any).
10909 
10910 Arguments:
10911 
10912     DeviceObject - the device object which requested the completion routine
10913 
10914     Irp - the irp being completed
10915 
10916     Context - unused
10917 
10918 Return Value:
10919 
10920     STATUS_MORE_PROCESSING_REQUIRED
10921 
10922 --*/
10923 NTSTATUS
10924 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
10925 ClasspSendSynchronousCompletion(
10926     IN PDEVICE_OBJECT DeviceObject,
10927     IN PIRP Irp,
10928     IN PVOID Context
10929     )
10930 {
10931     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,  "ClasspSendSynchronousCompletion: %p %p %p\n",
10932                    DeviceObject, Irp, Context));
10933     //
10934     // First set the status and information fields in the io status block
10935     // provided by the caller.
10936     //
10937 
10938     *(Irp->UserIosb) = Irp->IoStatus;
10939 
10940     //
10941     // Unlock the pages for the data buffer.
10942     //
10943 
10944     if(Irp->MdlAddress) {
10945         MmUnlockPages(Irp->MdlAddress);
10946         IoFreeMdl(Irp->MdlAddress);
10947     }
10948 
10949     //
10950     // Signal the caller's event.
10951     //
10952 
10953     KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
10954 
10955     //
10956     // Free the MDL and the IRP.
10957     //
10958 
10959     IoFreeIrp(Irp);
10960 
10961     return STATUS_MORE_PROCESSING_REQUIRED;
10962 } // end ClasspSendSynchronousCompletion()
10963 
10964 /*++
10965 
10966     ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
10967 
10968 --*/
10969 VOID
10970 ClasspRegisterMountedDeviceInterface(
10971     IN PDEVICE_OBJECT DeviceObject
10972     )
10973 {
10974 
10975     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
10976     BOOLEAN isFdo = commonExtension->IsFdo;
10977     PDEVICE_OBJECT pdo;
10978     UNICODE_STRING interfaceName;
10979 
10980     NTSTATUS status;
10981 
10982     PAGED_CODE();
10983 
10984     if(isFdo) {
10985 
10986         PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
10987 
10988         functionalExtension =
10989             (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
10990         pdo = functionalExtension->LowerPdo;
10991     } else {
10992         pdo = DeviceObject;
10993     }
10994 
10995 #ifdef _MSC_VER
10996 #pragma prefast(suppress:6014, "The allocated memory that interfaceName points to will be freed in ClassRemoveDevice().")
10997 #endif
10998     status = IoRegisterDeviceInterface(
10999                 pdo,
11000                 &MOUNTDEV_MOUNTED_DEVICE_GUID,
11001                 NULL,
11002                 &interfaceName
11003                 );
11004 
11005     if(NT_SUCCESS(status)) {
11006 
11007         //
11008         // Copy the interface name before setting the interface state - the
11009         // name is needed by the components we notify.
11010         //
11011 
11012         commonExtension->MountedDeviceInterfaceName = interfaceName;
11013         status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
11014 
11015         if(!NT_SUCCESS(status)) {
11016             RtlFreeUnicodeString(&interfaceName);
11017         }
11018     }
11019 
11020     if(!NT_SUCCESS(status)) {
11021         RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
11022                              NULL);
11023     }
11024     return;
11025 } // end ClasspRegisterMountedDeviceInterface()
11026 
11027 /*++////////////////////////////////////////////////////////////////////////////
11028 
11029 ClassSendDeviceIoControlSynchronous()
11030 
11031 Routine Description:
11032 
11033     This routine is based upon IoBuildDeviceIoControlRequest().  It has been
11034     modified to reduce code and memory by not double-buffering the io, using
11035     the same buffer for both input and output, allocating and deallocating
11036     the mdl on behalf of the caller, and waiting for the io to complete.
11037 
11038     This routine also works around the rare cases in which APC's are disabled.
11039     Since IoBuildDeviceIoControl() used APC's to signal completion, this had
11040     led to a number of difficult-to-detect hangs, where the irp was completed,
11041     but the event passed to IoBuild..() was still being waited upon by the
11042     caller.
11043 
11044 Arguments:
11045 
11046     IoControlCode - the IOCTL to send
11047 
11048     TargetDeviceObject - the device object that should handle the ioctl
11049 
11050     Buffer - the input and output buffer, or NULL if no input/output
11051 
11052     InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
11053 
11054     OutputBufferLength - the number of bytes to be filled in upon success
11055 
11056     InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
11057 
11058     IoStatus - the status block that contains the results of the operation
11059 
11060 Return Value:
11061 
11062 --*/
11063 VOID
11064 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11065 ClassSendDeviceIoControlSynchronous(
11066     _In_ ULONG IoControlCode,
11067     _In_ PDEVICE_OBJECT TargetDeviceObject,
11068     _Inout_updates_opt_(_Inexpressible_(max(InputBufferLength, OutputBufferLength))) PVOID Buffer,
11069     _In_ ULONG InputBufferLength,
11070     _In_ ULONG OutputBufferLength,
11071     _In_ BOOLEAN InternalDeviceIoControl,
11072     _Out_ PIO_STATUS_BLOCK IoStatus
11073     )
11074 {
11075     PIRP irp;
11076     PIO_STACK_LOCATION irpSp;
11077     ULONG method;
11078 
11079     PAGED_CODE();
11080 
11081     irp = NULL;
11082     method = IoControlCode & 3;
11083 
11084     #if DBG // Begin Argument Checking (nop in fre version)
11085 
11086     NT_ASSERT(ARGUMENT_PRESENT(IoStatus));
11087 
11088     if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
11089         NT_ASSERT(ARGUMENT_PRESENT(Buffer));
11090     }
11091     else {
11092         NT_ASSERT(!ARGUMENT_PRESENT(Buffer));
11093     }
11094     #endif
11095 
11096     //
11097     // Begin by allocating the IRP for this request.  Do not charge quota to
11098     // the current process for this IRP.
11099     //
11100 
11101     irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
11102     if (!irp) {
11103         IoStatus->Information = 0;
11104         IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
11105         return;
11106     }
11107 
11108     //
11109     // Get a pointer to the stack location of the first driver which will be
11110     // invoked.  This is where the function codes and the parameters are set.
11111     //
11112 
11113     irpSp = IoGetNextIrpStackLocation(irp);
11114 
11115     //
11116     // Set the major function code based on the type of device I/O control
11117     // function the caller has specified.
11118     //
11119 
11120     if (InternalDeviceIoControl) {
11121         irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
11122     } else {
11123         irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
11124     }
11125 
11126     //
11127     // Copy the caller's parameters to the service-specific portion of the
11128     // IRP for those parameters that are the same for all four methods.
11129     //
11130 
11131     irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
11132     irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
11133     irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
11134 
11135     //
11136     // Get the method bits from the I/O control code to determine how the
11137     // buffers are to be passed to the driver.
11138     //
11139 
11140     switch (method)
11141     {
11142         //
11143         // case 0
11144         //
11145         case METHOD_BUFFERED:
11146         {
11147             if ((InputBufferLength != 0) || (OutputBufferLength != 0))
11148             {
11149                 irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
11150                                                                         max(InputBufferLength, OutputBufferLength),
11151                                                                         CLASS_TAG_DEVICE_CONTROL);
11152                 if (irp->AssociatedIrp.SystemBuffer == NULL)
11153                 {
11154                     IoFreeIrp(irp);
11155 
11156                     IoStatus->Information = 0;
11157                     IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
11158                     return;
11159                 }
11160 
11161                 if (InputBufferLength != 0)
11162                 {
11163                     RtlCopyMemory(irp->AssociatedIrp.SystemBuffer, Buffer, InputBufferLength);
11164                 }
11165             }
11166 
11167             irp->UserBuffer = Buffer;
11168 
11169             break;
11170         }
11171 
11172         //
11173         // case 1, case 2
11174         //
11175         case METHOD_IN_DIRECT:
11176         case METHOD_OUT_DIRECT:
11177         {
11178             if (InputBufferLength != 0)
11179             {
11180                 irp->AssociatedIrp.SystemBuffer = Buffer;
11181             }
11182 
11183             if (OutputBufferLength != 0)
11184             {
11185                 irp->MdlAddress = IoAllocateMdl(Buffer,
11186                                                 OutputBufferLength,
11187                                                 FALSE,
11188                                                 FALSE,
11189                                                 (PIRP) NULL);
11190                 if (irp->MdlAddress == NULL)
11191                 {
11192                     IoFreeIrp(irp);
11193 
11194                     IoStatus->Information = 0;
11195                     IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
11196                     return;
11197                 }
11198 
11199                 _SEH2_TRY
11200                 {
11201                     MmProbeAndLockPages(irp->MdlAddress,
11202                                         KernelMode,
11203                                         (method == METHOD_IN_DIRECT) ? IoReadAccess : IoWriteAccess);
11204                 }
11205 #ifdef _MSC_VER
11206                 #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
11207 #endif
11208                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
11209                 {
11210                     IoFreeMdl(irp->MdlAddress);
11211                     IoFreeIrp(irp);
11212 
11213                     IoStatus->Information = 0;
11214                     IoStatus->Status = _SEH2_GetExceptionCode();
11215                     _SEH2_YIELD(return);
11216                 } _SEH2_END;
11217             }
11218 
11219             break;
11220         }
11221 
11222         //
11223         // case 3
11224         //
11225         case METHOD_NEITHER:
11226         {
11227             NT_ASSERT(!"ClassSendDeviceIoControlSynchronous does not support METHOD_NEITHER Ioctls");
11228 
11229             IoFreeIrp(irp);
11230 
11231             IoStatus->Information = 0;
11232             IoStatus->Status = STATUS_NOT_SUPPORTED;
11233             return;
11234         }
11235     }
11236 
11237     irp->Tail.Overlay.Thread = PsGetCurrentThread();
11238 
11239     //
11240     // send the irp synchronously
11241     //
11242 
11243     ClassSendIrpSynchronous(TargetDeviceObject, irp);
11244 
11245     //
11246     // copy the iostatus block for the caller
11247     //
11248 
11249     *IoStatus = irp->IoStatus;
11250 
11251     //
11252     // free any allocated resources
11253     //
11254 
11255     switch (method) {
11256         case METHOD_BUFFERED: {
11257 
11258             NT_ASSERT(irp->UserBuffer == Buffer);
11259 
11260             //
11261             // first copy the buffered result, if any
11262             // Note that there are no security implications in
11263             // not checking for success since only drivers can
11264             // call into this routine anyways...
11265             //
11266 
11267             if (OutputBufferLength != 0) {
11268 #ifdef _MSC_VER
11269                 #pragma warning(suppress: 6386) // Buffer's size is max(InputBufferLength, OutputBufferLength)
11270 #endif
11271                 RtlCopyMemory(Buffer, // irp->UserBuffer
11272                               irp->AssociatedIrp.SystemBuffer,
11273                               OutputBufferLength
11274                               );
11275             }
11276 
11277             //
11278             // then free the memory allocated to buffer the io
11279             //
11280 
11281             if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
11282                 FREE_POOL(irp->AssociatedIrp.SystemBuffer);
11283             }
11284             break;
11285         }
11286 
11287         case METHOD_IN_DIRECT:
11288         case METHOD_OUT_DIRECT: {
11289 
11290             //
11291             // we alloc a mdl if there is an output buffer specified
11292             // free it here after unlocking the pages
11293             //
11294 
11295             if (OutputBufferLength != 0) {
11296                 NT_ASSERT(irp->MdlAddress != NULL);
11297                 MmUnlockPages(irp->MdlAddress);
11298                 IoFreeMdl(irp->MdlAddress);
11299                 irp->MdlAddress = (PMDL) NULL;
11300             }
11301             break;
11302         }
11303 
11304         case METHOD_NEITHER: {
11305             NT_ASSERT(!"Code is out of date");
11306             break;
11307         }
11308     }
11309 
11310     //
11311     // we always have allocated an irp.  free it here.
11312     //
11313 
11314     IoFreeIrp(irp);
11315     irp = (PIRP) NULL;
11316 
11317     //
11318     // return the io status block's status to the caller
11319     //
11320 
11321     return;
11322 } // end ClassSendDeviceIoControlSynchronous()
11323 
11324 /*++////////////////////////////////////////////////////////////////////////////
11325 
11326 ClassForwardIrpSynchronous()
11327 
11328 Routine Description:
11329 
11330     Forwards a given irp to the next lower device object.
11331 
11332 Arguments:
11333 
11334     CommonExtension - the common class extension
11335 
11336     Irp - the request to forward down the stack
11337 
11338 Return Value:
11339 
11340 --*/
11341 NTSTATUS
11342 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11343 ClassForwardIrpSynchronous(
11344     _In_ PCOMMON_DEVICE_EXTENSION CommonExtension,
11345     _In_ PIRP Irp
11346     )
11347 {
11348     IoCopyCurrentIrpStackLocationToNext(Irp);
11349     return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
11350 } // end ClassForwardIrpSynchronous()
11351 
11352 /*++////////////////////////////////////////////////////////////////////////////
11353 
11354 ClassSendIrpSynchronous()
11355 
11356 Routine Description:
11357 
11358     This routine sends the given irp to the given device object, and waits for
11359     it to complete.  On debug versions, will print out a debug message and
11360     optionally assert for "lost" irps based upon classpnp's globals
11361 
11362 Arguments:
11363 
11364     TargetDeviceObject - the device object to handle this irp
11365 
11366     Irp - the request to be sent
11367 
11368 Return Value:
11369 
11370 --*/
11371 NTSTATUS
11372 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11373 ClassSendIrpSynchronous(
11374     _In_ PDEVICE_OBJECT TargetDeviceObject,
11375     _In_ PIRP Irp
11376     )
11377 {
11378     KEVENT event;
11379     NTSTATUS status;
11380 
11381     NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
11382     NT_ASSERT(TargetDeviceObject != NULL);
11383     NT_ASSERT(Irp != NULL);
11384     NT_ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
11385 
11386     //
11387     // ISSUE-2000/02/20-henrygab   What if APCs are disabled?
11388     //    May need to enter critical section before IoCallDriver()
11389     //    until the event is hit?
11390     //
11391 
11392     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
11393     IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
11394                            TRUE, TRUE, TRUE);
11395 
11396     status = IoCallDriver(TargetDeviceObject, Irp);
11397     _Analysis_assume_(status!=STATUS_PENDING);
11398     if (status == STATUS_PENDING) {
11399 
11400         #if DBG
11401             LARGE_INTEGER timeout;
11402 
11403             timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
11404                                           ClasspnpGlobals.SecondsToWaitForIrps);
11405 
11406             do {
11407                 status = KeWaitForSingleObject(&event,
11408                                                Executive,
11409                                                KernelMode,
11410                                                FALSE,
11411                                                &timeout);
11412 
11413 
11414                 if (status == STATUS_TIMEOUT) {
11415 
11416                     //
11417                     // This DebugPrint should almost always be investigated by the
11418                     // party who sent the irp and/or the current owner of the irp.
11419                     // Synchronous Irps should not take this long (currently 30
11420                     // seconds) without good reason.  This points to a potentially
11421                     // serious problem in the underlying device stack.
11422                     //
11423 
11424                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  "ClassSendIrpSynchronous: (%p) irp %p did not "
11425                                 "complete within %x seconds\n",
11426                                 TargetDeviceObject, Irp,
11427                                 ClasspnpGlobals.SecondsToWaitForIrps
11428                                 ));
11429 
11430                     if (ClasspnpGlobals.BreakOnLostIrps != 0) {
11431                         NT_ASSERT(!" - Irp failed to complete within 30 seconds - ");
11432                     }
11433                 }
11434 
11435 
11436             } while (status==STATUS_TIMEOUT);
11437         #else
11438             (VOID)KeWaitForSingleObject(&event,
11439                                         Executive,
11440                                         KernelMode,
11441                                         FALSE,
11442                                         NULL);
11443         #endif
11444 
11445         status = Irp->IoStatus.Status;
11446     }
11447 
11448     return status;
11449 } // end ClassSendIrpSynchronous()
11450 
11451 /*++////////////////////////////////////////////////////////////////////////////
11452 
11453 ClassGetVpb()
11454 
11455 Routine Description:
11456 
11457     This routine returns the current VPB (Volume Parameter Block) for the
11458     given device object.
11459     The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
11460     of DEVICE_OBJECT; hence this exported function.
11461 
11462 Arguments:
11463 
11464     DeviceObject - the device to get the VPB for
11465 
11466 Return Value:
11467 
11468     the VPB, or NULL if none.
11469 
11470 --*/
11471 PVPB
11472 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11473 ClassGetVpb(
11474     _In_ PDEVICE_OBJECT DeviceObject
11475     )
11476 {
11477 #ifdef _MSC_VER
11478 #pragma prefast(suppress:28175)
11479 #endif
11480     return DeviceObject->Vpb;
11481 } // end ClassGetVpb()
11482 
11483 /*++
11484 
11485     ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
11486 
11487 --*/
11488 NTSTATUS
11489 ClasspAllocateReleaseRequest(
11490     IN PDEVICE_OBJECT Fdo
11491     )
11492 {
11493     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
11494 
11495     PAGED_CODE();
11496 
11497     KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
11498 
11499     fdoExtension->ReleaseQueueNeeded = FALSE;
11500     fdoExtension->ReleaseQueueInProgress = FALSE;
11501     fdoExtension->ReleaseQueueIrpFromPool = FALSE;
11502 
11503     //
11504     // The class driver is responsible for allocating a properly sized irp,
11505     // or ClassReleaseQueue will attempt to do it on the first error.
11506     //
11507 
11508     fdoExtension->ReleaseQueueIrp = NULL;
11509 
11510     //
11511     // Write length to SRB.
11512     //
11513 
11514     fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
11515 
11516     return STATUS_SUCCESS;
11517 } // end ClasspAllocateReleaseRequest()
11518 
11519 /*++
11520 
11521     ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
11522 
11523 --*/
11524 VOID
11525 ClasspFreeReleaseRequest(
11526     IN PDEVICE_OBJECT Fdo
11527     )
11528 {
11529     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
11530 
11531     PAGED_CODE();
11532 
11533     //KIRQL oldIrql;
11534 
11535     NT_ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
11536 
11537     //
11538     // free anything the driver allocated
11539     //
11540 
11541     if (fdoExtension->ReleaseQueueIrp) {
11542         if (fdoExtension->ReleaseQueueIrpFromPool) {
11543             FREE_POOL(fdoExtension->ReleaseQueueIrp);
11544         } else {
11545             IoFreeIrp(fdoExtension->ReleaseQueueIrp);
11546         }
11547         fdoExtension->ReleaseQueueIrp = NULL;
11548     }
11549 
11550     //
11551     // free anything that we allocated
11552     //
11553 
11554     if ((fdoExtension->PrivateFdoData) &&
11555         (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
11556 
11557         FREE_POOL(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
11558         fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
11559     }
11560 
11561     return;
11562 } // end ClasspFreeReleaseRequest()
11563 
11564 /*++////////////////////////////////////////////////////////////////////////////
11565 
11566 ClassReleaseQueue()
11567 
11568 Routine Description:
11569 
11570     This routine issues an internal device control command
11571     to the port driver to release a frozen queue. The call
11572     is issued asynchronously as ClassReleaseQueue will be invoked
11573     from the IO completion DPC (and will have no context to
11574     wait for a synchronous call to complete).
11575 
11576     This routine must be called with the remove lock held.
11577 
11578 Arguments:
11579 
11580     Fdo - The functional device object for the device with the frozen queue.
11581 
11582 Return Value:
11583 
11584     None.
11585 
11586 --*/
11587 VOID
11588 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11589 ClassReleaseQueue(
11590     _In_ PDEVICE_OBJECT Fdo
11591     )
11592 {
11593     ClasspReleaseQueue(Fdo, NULL);
11594     return;
11595 } // end ClassReleaseQueue()
11596 
11597 /*++////////////////////////////////////////////////////////////////////////////
11598 
11599 ClasspAllocateReleaseQueueIrp()
11600 
11601 Routine Description:
11602 
11603     This routine allocates the release queue irp held in classpnp's private
11604     extension.  This was added to allow no-memory conditions to be more
11605     survivable.
11606 
11607 Return Value:
11608 
11609     NT_SUCCESS value.
11610 
11611 Notes:
11612 
11613     Does not grab the spinlock.  Should only be called from StartDevice()
11614     routine.  May be called elsewhere for poorly-behaved drivers that cause
11615     the queue to lockup before the device is started.  This should *never*
11616     occur, since it's illegal to send a request to a non-started PDO.  This
11617     condition is checked for in ClasspReleaseQueue().
11618 
11619 --*/
11620 NTSTATUS
11621 ClasspAllocateReleaseQueueIrp(
11622     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
11623     )
11624 {
11625     UCHAR lowerStackSize;
11626 
11627     //
11628     // do an initial check w/o the spinlock
11629     //
11630 
11631     if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
11632         return STATUS_SUCCESS;
11633     }
11634 
11635 
11636     lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
11637 
11638     //
11639     // don't allocate one if one is in progress!  this means whoever called
11640     // this routine didn't check if one was in progress.
11641     //
11642 
11643     NT_ASSERT(!(FdoExtension->ReleaseQueueInProgress));
11644 
11645     FdoExtension->PrivateFdoData->ReleaseQueueIrp =
11646         ExAllocatePoolWithTag(NonPagedPoolNx,
11647                               IoSizeOfIrp(lowerStackSize),
11648                               CLASS_TAG_RELEASE_QUEUE
11649                               );
11650 
11651     if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
11652         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,  "ClassPnpStartDevice: Cannot allocate for "
11653                     "release queue irp\n"));
11654         return STATUS_INSUFFICIENT_RESOURCES;
11655     }
11656     IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
11657                     IoSizeOfIrp(lowerStackSize),
11658                     lowerStackSize);
11659     FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
11660 
11661     return STATUS_SUCCESS;
11662 }
11663 
11664 /*++////////////////////////////////////////////////////////////////////////////
11665 
11666 ClasspAllocatePowerProcessIrp()
11667 
11668 Routine Description:
11669 
11670     This routine allocates the power process irp.
11671     This routine should be called after PrivateFdoData is allocated.
11672 
11673 Return Value:
11674 
11675     NTSTATUS value.
11676 
11677 Notes:
11678 
11679     Should only be called from StartDevice()
11680 
11681 --*/
11682 NTSTATUS
11683 ClasspAllocatePowerProcessIrp(
11684     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
11685     )
11686 {
11687     UCHAR stackSize;
11688 
11689     NT_ASSERT(FdoExtension->PrivateFdoData != NULL);
11690 
11691     stackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1;
11692 
11693     FdoExtension->PrivateFdoData->PowerProcessIrp = ExAllocatePoolWithTag(NonPagedPoolNx,
11694                                                                           IoSizeOfIrp(stackSize),
11695                                                                           CLASS_TAG_POWER
11696                                                                           );
11697 
11698     if (FdoExtension->PrivateFdoData->PowerProcessIrp == NULL) {
11699 
11700         return STATUS_INSUFFICIENT_RESOURCES;
11701     } else {
11702 
11703         IoInitializeIrp(FdoExtension->PrivateFdoData->PowerProcessIrp,
11704                         IoSizeOfIrp(stackSize),
11705                         stackSize);
11706 
11707         return STATUS_SUCCESS;
11708     }
11709 }
11710 
11711 
11712 /*++////////////////////////////////////////////////////////////////////////////
11713 
11714 ClasspReleaseQueue()
11715 
11716 Routine Description:
11717 
11718     This routine issues an internal device control command
11719     to the port driver to release a frozen queue. The call
11720     is issued asynchronously as ClassReleaseQueue will be invoked
11721     from the IO completion DPC (and will have no context to
11722     wait for a synchronous call to complete).
11723 
11724     This routine must be called with the remove lock held.
11725 
11726 Arguments:
11727 
11728     Fdo - The functional device object for the device with the frozen queue.
11729 
11730     ReleaseQueueIrp - If this irp is supplied then the test to determine whether
11731                       a release queue request is in progress will be ignored.
11732                       The irp provided must be the IRP originally allocated
11733                       for release queue requests (so this parameter can only
11734                       really be provided by the release queue completion
11735                       routine.)
11736 
11737 Return Value:
11738 
11739     None.
11740 
11741 --*/
11742 VOID
11743 ClasspReleaseQueue(
11744     IN PDEVICE_OBJECT Fdo,
11745     IN PIRP ReleaseQueueIrp OPTIONAL
11746     )
11747 {
11748     PIO_STACK_LOCATION irpStack;
11749     PIRP irp;
11750     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
11751     PDEVICE_OBJECT lowerDevice;
11752     PSTORAGE_REQUEST_BLOCK_HEADER srb;
11753     KIRQL currentIrql;
11754     ULONG function;
11755 
11756     lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
11757 
11758     //
11759     // we raise irql seperately so we're not swapped out or suspended
11760     // while holding the release queue irp in this routine.  this lets
11761     // us release the spin lock before lowering irql.
11762     //
11763 
11764     KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
11765 
11766     KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
11767 
11768     //
11769     // make sure that if they passed us an irp, it matches our allocated irp.
11770     //
11771 
11772     NT_ASSERT((ReleaseQueueIrp == NULL) ||
11773            (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
11774 
11775     //
11776     // ASSERT that we've already allocated this. (should not occur)
11777     // try to allocate it anyways, then finally bugcheck if
11778     // there's still no memory...
11779     //
11780 
11781     NT_ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
11782     if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
11783         ClasspAllocateReleaseQueueIrp(fdoExtension);
11784     }
11785     if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
11786         KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
11787     }
11788 
11789     if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
11790 
11791         //
11792         // Someone is already using the irp - just set the flag to indicate that
11793         // we need to release the queue again.
11794         //
11795 
11796         fdoExtension->ReleaseQueueNeeded = TRUE;
11797         KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
11798         KeLowerIrql(currentIrql);
11799         return;
11800 
11801     }
11802 
11803     //
11804     // Mark that there is a release queue in progress and drop the spinlock.
11805     //
11806 
11807     fdoExtension->ReleaseQueueInProgress = TRUE;
11808     if (ReleaseQueueIrp) {
11809         irp = ReleaseQueueIrp;
11810     } else {
11811         irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
11812     }
11813 
11814     if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
11815         srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->ReleaseQueueSrb.SrbEx);
11816     } else {
11817         srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->ReleaseQueueSrb);
11818     }
11819 
11820     KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
11821 
11822     NT_ASSERT(irp != NULL);
11823 
11824     irpStack = IoGetNextIrpStackLocation(irp);
11825 
11826     irpStack->MajorFunction = IRP_MJ_SCSI;
11827 
11828     SrbSetOriginalRequest(srb, irp);
11829 
11830     //
11831     // Store the SRB address in next stack for port driver.
11832     //
11833 
11834     irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb;
11835 
11836     //
11837     // If this device is removable then flush the queue.  This will also
11838     // release it.
11839     //
11840 
11841     if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
11842        function = SRB_FUNCTION_FLUSH_QUEUE;
11843     }
11844     else {
11845        function = SRB_FUNCTION_RELEASE_QUEUE;
11846     }
11847 
11848     if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
11849         ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = function;
11850     } else {
11851         srb->Function = (UCHAR)function;
11852     }
11853 
11854     ClassAcquireRemoveLock(Fdo, irp);
11855 
11856     IoSetCompletionRoutine(irp,
11857                            ClassReleaseQueueCompletion,
11858                            Fdo,
11859                            TRUE,
11860                            TRUE,
11861                            TRUE);
11862 
11863     IoCallDriver(lowerDevice, irp);
11864 
11865     KeLowerIrql(currentIrql);
11866 
11867     return;
11868 
11869 } // end ClassReleaseQueue()
11870 
11871 /*++////////////////////////////////////////////////////////////////////////////
11872 
11873 ClassReleaseQueueCompletion()
11874 
11875 Routine Description:
11876 
11877     This routine is called when an asynchronous I/O request
11878     which was issused by the class driver completes.  Examples of such requests
11879     are release queue or START UNIT. This routine releases the queue if
11880     necessary.  It then frees the context and the IRP.
11881 
11882 Arguments:
11883 
11884     DeviceObject - The device object for the logical unit; however since this
11885         is the top stack location the value is NULL.
11886 
11887     Irp - Supplies a pointer to the Irp to be processed.
11888 
11889     Context - Supplies the context to be used to process this request.
11890 
11891 Return Value:
11892 
11893     None.
11894 
11895 --*/
11896 NTSTATUS
11897 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11898 ClassReleaseQueueCompletion(
11899     PDEVICE_OBJECT DeviceObject,
11900     PIRP Irp,
11901     PVOID Context
11902     )
11903 {
11904     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
11905     KIRQL oldIrql;
11906 
11907     BOOLEAN releaseQueueNeeded;
11908 
11909     if (Context == NULL) {
11910         NT_ASSERT(Context != NULL);
11911         return STATUS_INVALID_PARAMETER;
11912     }
11913 
11914     DeviceObject = Context;
11915 
11916     fdoExtension = DeviceObject->DeviceExtension;
11917 
11918     ClassReleaseRemoveLock(DeviceObject, Irp);
11919 
11920     //
11921     // Grab the spinlock and clear the release queue in progress flag so others
11922     // can run.  Save (and clear) the state of the release queue needed flag
11923     // so that we can issue a new release queue outside the spinlock.
11924     //
11925 
11926     KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
11927 
11928     releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
11929 
11930     fdoExtension->ReleaseQueueNeeded = FALSE;
11931     fdoExtension->ReleaseQueueInProgress = FALSE;
11932 
11933     KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
11934 
11935     //
11936     // If we need a release queue then issue one now.  Another processor may
11937     // have already started one in which case we'll try to issue this one after
11938     // it is done - but we should never recurse more than one deep.
11939     //
11940 
11941     if(releaseQueueNeeded) {
11942         ClasspReleaseQueue(DeviceObject, Irp);
11943     }
11944 
11945     //
11946     // Indicate the I/O system should stop processing the Irp completion.
11947     //
11948 
11949     return STATUS_MORE_PROCESSING_REQUIRED;
11950 
11951 } // ClassAsynchronousCompletion()
11952 
11953 
11954 /*++////////////////////////////////////////////////////////////////////////////
11955 
11956 ClassAcquireChildLock()
11957 
11958 Routine Description:
11959 
11960     This routine acquires the lock protecting children PDOs.  It may be
11961     acquired recursively by the same thread, but must be release by the
11962     thread once for each acquisition.
11963 
11964 Arguments:
11965 
11966     FdoExtension - the device whose child list is protected.
11967 
11968 Return Value:
11969 
11970     None
11971 
11972 --*/
11973 _IRQL_requires_max_(PASSIVE_LEVEL)
11974 VOID
11975 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
11976 ClassAcquireChildLock(
11977     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
11978     )
11979 {
11980     PAGED_CODE();
11981 
11982     if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
11983         (VOID)KeWaitForSingleObject(&FdoExtension->ChildLock,
11984                                     Executive, KernelMode,
11985                                     FALSE, NULL);
11986 
11987         NT_ASSERT(FdoExtension->ChildLockOwner == NULL);
11988         NT_ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
11989 
11990         FdoExtension->ChildLockOwner = KeGetCurrentThread();
11991     } else {
11992         NT_ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
11993     }
11994 
11995     FdoExtension->ChildLockAcquisitionCount++;
11996     return;
11997 }
11998 
11999 /*++////////////////////////////////////////////////////////////////////////////
12000 
12001 ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
12002 
12003 Routine Description:
12004 
12005     This routine releases the lock protecting children PDOs.  It must be
12006     called once for each time ClassAcquireChildLock was called.
12007 
12008 Arguments:
12009 
12010     FdoExtension - the device whose child list is protected
12011 
12012 Return Value:
12013 
12014     None.
12015 
12016 --*/
12017 VOID
12018 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
12019 ClassReleaseChildLock(
12020     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
12021     )
12022 {
12023     NT_ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
12024     NT_ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
12025 
12026     FdoExtension->ChildLockAcquisitionCount -= 1;
12027 
12028     if(FdoExtension->ChildLockAcquisitionCount == 0) {
12029         FdoExtension->ChildLockOwner = NULL;
12030         KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
12031     }
12032 
12033     return;
12034 } // end ClassReleaseChildLock(
12035 
12036 /*++////////////////////////////////////////////////////////////////////////////
12037 
12038 ClassAddChild()
12039 
12040 Routine Description:
12041 
12042     This routine will insert a new child into the head of the child list.
12043 
12044 Arguments:
12045 
12046     Parent - the child's parent (contains the head of the list)
12047     Child - the child to be inserted.
12048     AcquireLock - whether the child lock should be acquired (TRUE) or whether
12049                   it's already been acquired by or on behalf of the caller
12050                   (FALSE).
12051 
12052 Return Value:
12053 
12054     None.
12055 
12056 --*/
12057 VOID
12058 ClassAddChild(
12059     _In_ PFUNCTIONAL_DEVICE_EXTENSION Parent,
12060     _In_ PPHYSICAL_DEVICE_EXTENSION Child,
12061     _In_ BOOLEAN AcquireLock
12062     )
12063 {
12064     if(AcquireLock) {
12065         ClassAcquireChildLock(Parent);
12066     }
12067 
12068     #if DBG
12069         //
12070         // Make sure this child's not already in the list.
12071         //
12072         {
12073             PPHYSICAL_DEVICE_EXTENSION testChild;
12074 
12075             for (testChild = Parent->CommonExtension.ChildList;
12076                  testChild != NULL;
12077                  testChild = testChild->CommonExtension.ChildList) {
12078 
12079                 NT_ASSERT(testChild != Child);
12080             }
12081         }
12082     #endif
12083 
12084     Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
12085     Parent->CommonExtension.ChildList = Child;
12086 
12087     if(AcquireLock) {
12088         ClassReleaseChildLock(Parent);
12089     }
12090     return;
12091 } // end ClassAddChild()
12092 
12093 /*++////////////////////////////////////////////////////////////////////////////
12094 
12095 ClassRemoveChild()
12096 
12097 Routine Description:
12098 
12099     This routine will remove a child from the child list.
12100 
12101 Arguments:
12102 
12103     Parent - the parent to be removed from.
12104 
12105     Child - the child to be removed or NULL if the first child should be
12106             removed.
12107 
12108     AcquireLock - whether the child lock should be acquired (TRUE) or whether
12109                   it's already been acquired by or on behalf of the caller
12110                   (FALSE).
12111 
12112 Return Value:
12113 
12114     A pointer to the child which was removed or NULL if no such child could
12115     be found in the list (or if Child was NULL but the list is empty).
12116 
12117 --*/
12118 PPHYSICAL_DEVICE_EXTENSION
12119 ClassRemoveChild(
12120     IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
12121     IN PPHYSICAL_DEVICE_EXTENSION Child,
12122     IN BOOLEAN AcquireLock
12123     )
12124 {
12125     if(AcquireLock) {
12126         ClassAcquireChildLock(Parent);
12127     }
12128 
12129     TRY {
12130         PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
12131 
12132         //
12133         // If the list is empty then bail out now.
12134         //
12135 
12136         if(Parent->CommonExtension.ChildList == NULL) {
12137             Child = NULL;
12138             LEAVE;
12139         }
12140 
12141         //
12142         // If the caller specified a child then find the child object before
12143         // it.  If none was specified then the FDO is the child object before
12144         // the one we want to remove.
12145         //
12146 
12147         if(Child != NULL) {
12148 
12149             //
12150             // Scan through the child list to find the entry which points to
12151             // this one.
12152             //
12153 
12154             do {
12155                 NT_ASSERT(previousChild != &Child->CommonExtension);
12156 
12157                 if(previousChild->ChildList == Child) {
12158                     break;
12159                 }
12160 
12161                 previousChild = &previousChild->ChildList->CommonExtension;
12162             } while(previousChild != NULL);
12163 
12164             if(previousChild == NULL) {
12165                 Child = NULL;
12166                 LEAVE;
12167             }
12168         }
12169 
12170         //
12171         // Save the next child away then unlink it from the list.
12172         //
12173 
12174         Child = previousChild->ChildList;
12175         previousChild->ChildList = Child->CommonExtension.ChildList;
12176         Child->CommonExtension.ChildList = NULL;
12177 
12178     } FINALLY {
12179         if(AcquireLock) {
12180             ClassReleaseChildLock(Parent);
12181         }
12182     }
12183     return Child;
12184 } // end ClassRemoveChild()
12185 
12186 
12187 /*++
12188 
12189     ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
12190 
12191 --*/
12192 VOID
12193 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
12194 ClasspRetryRequestDpc(
12195     IN PKDPC Dpc,
12196     IN PVOID DeferredContext,
12197     IN PVOID Arg1,
12198     IN PVOID Arg2
12199     )
12200 {
12201     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
12202     PCOMMON_DEVICE_EXTENSION commonExtension;
12203     PCLASS_PRIVATE_FDO_DATA fdoData;
12204     PCLASS_RETRY_INFO retryList;
12205     KIRQL irql;
12206     PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)DeferredContext;
12207 
12208     UNREFERENCED_PARAMETER(Dpc);
12209     UNREFERENCED_PARAMETER(Arg1);
12210     UNREFERENCED_PARAMETER(Arg2);
12211 
12212     if (DeferredContext == NULL) {
12213         NT_ASSERT(DeferredContext != NULL);
12214         return;
12215     }
12216 
12217     commonExtension = DeviceObject->DeviceExtension;
12218     NT_ASSERT(commonExtension->IsFdo);
12219     fdoExtension = DeviceObject->DeviceExtension;
12220     fdoData = fdoExtension->PrivateFdoData;
12221 
12222 
12223     KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
12224     {
12225         LARGE_INTEGER now;
12226         KeQueryTickCount(&now);
12227 
12228         //
12229         // if CurrentTick is less than now
12230         //      fire another DPC
12231         // else
12232         //      retry entire list
12233         // endif
12234         //
12235 
12236         if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
12237 
12238             ClasspRetryDpcTimer(fdoData);
12239             retryList = NULL;
12240 
12241         } else {
12242 
12243             retryList = fdoData->Retry.ListHead;
12244             fdoData->Retry.ListHead = NULL;
12245             fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
12246             fdoData->Retry.Tick.QuadPart  = (LONGLONG)0;
12247 
12248         }
12249     }
12250     KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
12251 
12252     while (retryList != NULL) {
12253 
12254         PIRP irp;
12255 
12256 
12257         irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
12258         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry:  -- %p\n", irp));
12259         retryList = retryList->Next;
12260         #if DBG
12261             irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
12262             irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
12263             irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
12264             irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
12265         #endif
12266 
12267 
12268         if (NO_REMOVE == InterlockedCompareExchange((volatile LONG *)&commonExtension->IsRemoved, REMOVE_PENDING, REMOVE_PENDING)) {
12269 
12270             IoCallDriver(commonExtension->LowerDeviceObject, irp);
12271 
12272         } else {
12273 
12274             PIO_STACK_LOCATION irpStack;
12275 
12276             //
12277             // Ensure that we don't skip a completion routine (equivalent of sending down a request
12278             // to a device after it has received a remove and it completes the request. We need to
12279             // mimic that behavior here).
12280             //
12281             IoSetNextIrpStackLocation(irp);
12282 
12283             irpStack = IoGetCurrentIrpStackLocation(irp);
12284 
12285             if (irpStack->MajorFunction == IRP_MJ_SCSI) {
12286 
12287                 PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
12288 
12289                 if (srb) {
12290                     srb->SrbStatus = SRB_STATUS_NO_DEVICE;
12291                 }
12292             }
12293 
12294             //
12295             // Ensure that no retries will take place. This takes care of requests that are either
12296             // not IRP_MJ_SCSI or for ones where the SRB was passed in to the completion routine
12297             // as a context as opposed to an argument on the IRP stack location.
12298             //
12299             irpStack->Parameters.Others.Argument4 = (PVOID)0;
12300 
12301             irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
12302             irp->IoStatus.Information = 0;
12303             IoCompleteRequest(irp,  IO_NO_INCREMENT);
12304         }
12305 
12306     }
12307     return;
12308 
12309 } // end ClasspRetryRequestDpc()
12310 
12311 /*++
12312 
12313     ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
12314 
12315 --*/
12316 VOID
12317 ClassRetryRequest(
12318     IN PDEVICE_OBJECT SelfDeviceObject,
12319     IN PIRP           Irp,
12320     _In_ _In_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS) // 100 seconds; already an assert on this...
12321     IN LONGLONG       TimeDelta100ns // in 100ns units
12322     )
12323 {
12324     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
12325     PCLASS_PRIVATE_FDO_DATA fdoData;
12326     PCLASS_RETRY_INFO  retryInfo;
12327     LARGE_INTEGER      delta;
12328     KIRQL irql;
12329 
12330     //
12331     // this checks we aren't destroying irps
12332     //
12333     NT_ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
12334 
12335     fdoExtension = SelfDeviceObject->DeviceExtension;
12336 
12337     if (!fdoExtension->CommonExtension.IsFdo) {
12338 
12339         //
12340         // this debug print/assertion should ALWAYS be investigated.
12341         // ClassRetryRequest can currently only be used by FDO's
12342         //
12343 
12344         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
12345         NT_ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
12346         return;
12347 
12348     }
12349 
12350     fdoData = fdoExtension->PrivateFdoData;
12351 
12352     if (TimeDelta100ns < 0) {
12353         NT_ASSERT(!"ClassRetryRequest - must use positive delay");
12354         TimeDelta100ns *= -1;
12355     }
12356 
12357     /*
12358      *  We are going to queue the irp and send it down in a timer DPC.
12359      *  This means that we may be causing the irp to complete on a different thread than the issuing thread.
12360      *  So mark the irp pending.
12361      */
12362     IoMarkIrpPending(Irp);
12363 
12364     //
12365     // prepare what we can out of the loop
12366     //
12367 
12368     retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
12369     RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
12370 
12371     delta.QuadPart = (TimeDelta100ns / fdoData->Retry.Granularity);
12372     if (TimeDelta100ns % fdoData->Retry.Granularity) {
12373         delta.QuadPart ++; // round up to next tick
12374     }
12375     if (delta.QuadPart == (LONGLONG)0) {
12376         delta.QuadPart = MINIMUM_RETRY_UNITS;
12377     }
12378 
12379     //
12380     // now determine if we should fire another DPC or not
12381     //
12382 
12383     KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
12384 
12385     //
12386     // always add request to the list
12387     //
12388 
12389     retryInfo->Next = fdoData->Retry.ListHead;
12390     fdoData->Retry.ListHead = retryInfo;
12391 
12392     if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
12393 
12394         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: +++ %p\n", Irp));
12395 
12396         //
12397         // must be exactly one item on list
12398         //
12399 
12400         NT_ASSERT(fdoData->Retry.ListHead       != NULL);
12401         NT_ASSERT(fdoData->Retry.ListHead->Next == NULL);
12402 
12403         //
12404         // if currentDelta is zero, always fire a DPC
12405         //
12406 
12407         KeQueryTickCount(&fdoData->Retry.Tick);
12408         fdoData->Retry.Tick.QuadPart  += delta.QuadPart;
12409         fdoData->Retry.Delta.QuadPart  = delta.QuadPart;
12410         ClasspRetryDpcTimer(fdoData);
12411 
12412     } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
12413 
12414         //
12415         // if delta is greater than the list's current delta,
12416         // increase the DPC handling time by difference
12417         // and update the delta to new larger value
12418         // allow the DPC to re-fire itself if needed
12419         //
12420 
12421         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry:  ++ %p\n", Irp));
12422 
12423         //
12424         // must be at least two items on list
12425         //
12426 
12427         NT_ASSERT(fdoData->Retry.ListHead       != NULL);
12428         NT_ASSERT(fdoData->Retry.ListHead->Next != NULL);
12429 
12430         fdoData->Retry.Tick.QuadPart  -= fdoData->Retry.Delta.QuadPart;
12431         fdoData->Retry.Tick.QuadPart  += delta.QuadPart;
12432 
12433         fdoData->Retry.Delta.QuadPart  = delta.QuadPart;
12434 
12435     } else {
12436 
12437         //
12438         // just inserting it on the list was enough
12439         //
12440 
12441         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry:  ++ %p\n", Irp));
12442 
12443     }
12444 
12445 
12446     KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
12447 
12448 
12449 } // end ClassRetryRequest()
12450 
12451 /*++
12452 
12453     ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
12454 
12455 --*/
12456 VOID
12457 ClasspRetryDpcTimer(
12458     IN PCLASS_PRIVATE_FDO_DATA FdoData
12459     )
12460 {
12461     LARGE_INTEGER fire;
12462 
12463     NT_ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
12464     NT_ASSERT(FdoData->Retry.ListHead      != NULL);  // never fire an empty list
12465 
12466     //
12467     // fire == (CurrentTick - now) * (100ns per tick)
12468     //
12469     // NOTE: Overflow is nearly impossible and is ignored here
12470     //
12471 
12472     KeQueryTickCount(&fire);
12473     fire.QuadPart =  FdoData->Retry.Tick.QuadPart - fire.QuadPart;
12474     fire.QuadPart *= FdoData->Retry.Granularity;
12475 
12476     //
12477     // fire is now multiples of 100ns until should fire the timer.
12478     // if timer should already have expired, or would fire too quickly,
12479     // fire it in some arbitrary number of ticks to prevent infinitely
12480     // recursing.
12481     //
12482 
12483     if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
12484         fire.QuadPart = MINIMUM_RETRY_UNITS;
12485     }
12486 
12487     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
12488                 "ClassRetry: ======= %I64x ticks\n",
12489                 fire.QuadPart));
12490 
12491     //
12492     // must use negative to specify relative time to fire
12493     //
12494 
12495     fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
12496 
12497     //
12498     // set the timer, since this is the first addition
12499     //
12500 
12501     KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
12502 
12503     return;
12504 } // end ClasspRetryDpcTimer()
12505 
12506 NTSTATUS
12507 ClasspInitializeHotplugInfo(
12508     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
12509     )
12510 {
12511     PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
12512     DEVICE_REMOVAL_POLICY deviceRemovalPolicy = 0;
12513     NTSTATUS status;
12514     ULONG resultLength = 0;
12515     ULONG writeCacheOverride;
12516 
12517     PAGED_CODE();
12518 
12519     //
12520     // start with some default settings
12521     //
12522     RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
12523 
12524     //
12525     // set the size (aka version)
12526     //
12527 
12528     fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
12529 
12530     //
12531     // set if the device has removable media
12532     //
12533 
12534     if (FdoExtension->DeviceDescriptor->RemovableMedia) {
12535         fdoData->HotplugInfo.MediaRemovable = TRUE;
12536     } else {
12537         fdoData->HotplugInfo.MediaRemovable = FALSE;
12538     }
12539 
12540     //
12541     // this refers to devices which, for reasons not yet understood,
12542     // do not fail PREVENT_MEDIA_REMOVAL requests even though they
12543     // have no way to lock the media into the drive.  this allows
12544     // the filesystems to turn off delayed-write caching for these
12545     // devices as well.
12546     //
12547 
12548     if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
12549                   FDO_HACK_CANNOT_LOCK_MEDIA)) {
12550         fdoData->HotplugInfo.MediaHotplug = TRUE;
12551     } else {
12552         fdoData->HotplugInfo.MediaHotplug = FALSE;
12553     }
12554 
12555     //
12556     // Query the default removal policy from the kernel
12557     //
12558 
12559     status = IoGetDeviceProperty(FdoExtension->LowerPdo,
12560                                  DevicePropertyRemovalPolicy,
12561                                  sizeof(DEVICE_REMOVAL_POLICY),
12562                                  (PVOID)&deviceRemovalPolicy,
12563                                  &resultLength);
12564     if (!NT_SUCCESS(status)) {
12565         return status;
12566     }
12567 
12568     if (resultLength != sizeof(DEVICE_REMOVAL_POLICY)) {
12569         return STATUS_UNSUCCESSFUL;
12570     }
12571 
12572     //
12573     // Look into the registry to see if the user has chosen
12574     // to override the default setting for the removal policy.
12575     // User can override only if the default removal policy is
12576     // orderly or suprise removal.
12577 
12578     if ((deviceRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
12579         (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)) {
12580 
12581         DEVICE_REMOVAL_POLICY userRemovalPolicy = 0;
12582 
12583         ClassGetDeviceParameter(FdoExtension,
12584                                 CLASSP_REG_SUBKEY_NAME,
12585                                 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
12586                                 (PULONG)&userRemovalPolicy);
12587 
12588         //
12589         // Validate the override value and use it only if it is an
12590         // allowed value.
12591         //
12592         if ((userRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
12593             (userRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)) {
12594             deviceRemovalPolicy = userRemovalPolicy;
12595         }
12596     }
12597 
12598     //
12599     // use this info to set the DeviceHotplug setting
12600     // don't rely on DeviceCapabilities, since it can't properly
12601     // determine device relations, etc.  let the kernel figure this
12602     // stuff out instead.
12603     //
12604 
12605     if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
12606         fdoData->HotplugInfo.DeviceHotplug = TRUE;
12607     } else {
12608         fdoData->HotplugInfo.DeviceHotplug = FALSE;
12609     }
12610 
12611     //
12612     // this refers to the *filesystem* caching, but has to be included
12613     // here since it's a per-device setting.  this may change to be
12614     // stored by the system in the future.
12615     //
12616 
12617     writeCacheOverride = FALSE;
12618     ClassGetDeviceParameter(FdoExtension,
12619                             CLASSP_REG_SUBKEY_NAME,
12620                             CLASSP_REG_WRITE_CACHE_VALUE_NAME,
12621                             &writeCacheOverride);
12622 
12623     if (writeCacheOverride) {
12624         fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
12625     } else {
12626         fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
12627     }
12628 
12629     return STATUS_SUCCESS;
12630 }
12631 
12632 VOID
12633 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
12634 ClasspScanForClassHacks(
12635     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
12636     IN ULONG_PTR Data
12637     )
12638 {
12639     PAGED_CODE();
12640 
12641     //
12642     // remove invalid flags and save
12643     //
12644 
12645     CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
12646     SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
12647     return;
12648 }
12649 
12650 VOID
12651 ClasspScanForSpecialInRegistry(
12652     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
12653     )
12654 {
12655     HANDLE             deviceParameterHandle; // device instance key
12656     HANDLE             classParameterHandle; // classpnp subkey
12657     OBJECT_ATTRIBUTES  objectAttributes = {0};
12658     UNICODE_STRING     subkeyName;
12659     NTSTATUS           status;
12660 
12661     //
12662     // seeded in the ENUM tree by ClassInstaller
12663     //
12664     ULONG deviceHacks;
12665     RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0}; // null terminated array
12666 
12667     PAGED_CODE();
12668 
12669     deviceParameterHandle = NULL;
12670     classParameterHandle = NULL;
12671     deviceHacks = 0;
12672 
12673     status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
12674                                      PLUGPLAY_REGKEY_DEVICE,
12675                                      KEY_WRITE,
12676                                      &deviceParameterHandle
12677                                      );
12678 
12679     if (!NT_SUCCESS(status)) {
12680         goto cleanupScanForSpecial;
12681     }
12682 
12683     RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
12684     InitializeObjectAttributes(&objectAttributes,
12685                                &subkeyName,
12686                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
12687                                deviceParameterHandle,
12688                                NULL
12689                                );
12690 
12691     status = ZwOpenKey( &classParameterHandle,
12692                         KEY_READ,
12693                         &objectAttributes
12694                         );
12695 
12696     if (!NT_SUCCESS(status)) {
12697         goto cleanupScanForSpecial;
12698     }
12699 
12700     //
12701     // Setup the structure to read
12702     //
12703 
12704     queryTable[0].Flags         = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
12705     queryTable[0].Name          = CLASSP_REG_HACK_VALUE_NAME;
12706     queryTable[0].EntryContext  = &deviceHacks;
12707     queryTable[0].DefaultType   = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD;
12708     queryTable[0].DefaultData   = &deviceHacks;
12709     queryTable[0].DefaultLength = 0;
12710 
12711     //
12712     // read values
12713     //
12714 
12715     status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
12716                                     (PWSTR)classParameterHandle,
12717                                     &queryTable[0],
12718                                     NULL,
12719                                     NULL
12720                                     );
12721     if (!NT_SUCCESS(status)) {
12722         goto cleanupScanForSpecial;
12723     }
12724 
12725     //
12726     // remove unknown values and save...
12727     //
12728 
12729     CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
12730     SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
12731 
12732 
12733 cleanupScanForSpecial:
12734 
12735     if (deviceParameterHandle) {
12736         ZwClose(deviceParameterHandle);
12737     }
12738 
12739     if (classParameterHandle) {
12740         ZwClose(classParameterHandle);
12741     }
12742 
12743     //
12744     // we should modify the system hive to include another key for us to grab
12745     // settings from.  in this case:  Classpnp\HackFlags
12746     //
12747     // the use of a DWORD value for the HackFlags allows 32 hacks w/o
12748     // significant use of the registry, and also reduces OEM exposure.
12749     //
12750     // definition of bit flags:
12751     //   0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
12752     //                 cannot actually prevent removal.
12753     //   0x00000002 -- Device hard-hangs or times out for GESN requests.
12754     //   0xfffffffc -- Currently reserved, may be used later.
12755     //
12756 
12757     return;
12758 }
12759 
12760 /*++////////////////////////////////////////////////////////////////////////////
12761 
12762 ClasspUpdateDiskProperties()
12763 
12764 Routine Description:
12765 
12766     This routine will send IOCTL_DISK_UPDATE_PROPERTIES to top of stack - Partition Manager
12767     to invalidate the cached geometry.
12768 
12769 Arguments:
12770 
12771     Fdo - The device object whose capacity needs to be verified.
12772 
12773 Return Value:
12774 
12775     NTSTATUS
12776 
12777 --*/
12778 
12779 VOID
12780 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
12781 ClasspUpdateDiskProperties(
12782     IN PDEVICE_OBJECT Fdo,
12783     IN PVOID Context
12784     )
12785 {
12786     PDEVICE_OBJECT topOfStack;
12787     IO_STATUS_BLOCK ioStatus;
12788     NTSTATUS status = STATUS_SUCCESS;
12789     KEVENT event;
12790     PIRP irp = NULL;
12791     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
12792     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
12793     PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;
12794 
12795     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
12796 
12797     topOfStack = IoGetAttachedDeviceReference(Fdo);
12798 
12799     //
12800     // Send down irp to update properties
12801     //
12802 
12803     irp = IoBuildDeviceIoControlRequest(
12804                     IOCTL_DISK_UPDATE_PROPERTIES,
12805                     topOfStack,
12806                     NULL,
12807                     0,
12808                     NULL,
12809                     0,
12810                     FALSE,
12811                     &event,
12812                     &ioStatus);
12813 
12814     if (irp != NULL) {
12815 
12816 
12817         status = IoCallDriver(topOfStack, irp);
12818         if (status == STATUS_PENDING) {
12819             (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
12820             status = ioStatus.Status;
12821         }
12822 
12823     } else {
12824         status = STATUS_INSUFFICIENT_RESOURCES;
12825     }
12826 
12827     InterlockedExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 0);
12828 
12829     if (!NT_SUCCESS(status)) {
12830         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClasspUpdateDiskProperties: Disk property update for fdo %p failed with status 0x%X.\n", Fdo, status));
12831     } else {
12832         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspUpdateDiskProperties: Drive capacity has changed for %p.\n", Fdo));
12833     }
12834     ObDereferenceObject(topOfStack);
12835 
12836     if (WorkItem != NULL) {
12837         IoFreeWorkItem(WorkItem);
12838     }
12839 
12840     return;
12841 }
12842 
12843 BOOLEAN
12844 InterpretSenseInfoWithoutHistory(
12845     _In_ PDEVICE_OBJECT Fdo,
12846     _In_opt_ PIRP OriginalRequest,
12847     _In_ PSCSI_REQUEST_BLOCK Srb,
12848          UCHAR MajorFunctionCode,
12849          ULONG IoDeviceCode,
12850          ULONG PreviousRetryCount,
12851     _Out_ NTSTATUS * Status,
12852     _Out_opt_ _Deref_out_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
12853          LONGLONG * RetryIn100nsUnits
12854     )
12855 {
12856     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
12857     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
12858     LONGLONG tmpRetry = 0;
12859     BOOLEAN retry = FALSE;
12860 
12861     if (fdoData->InterpretSenseInfo != NULL)
12862     {
12863         SCSI_REQUEST_BLOCK tempSrb = {0};
12864         PSCSI_REQUEST_BLOCK srbPtr = Srb;
12865 
12866         // SAL annotations and ClassInitializeEx() both validate this
12867         NT_ASSERT(fdoData->InterpretSenseInfo->Interpret != NULL);
12868 
12869         //
12870         // If class driver does not support extended SRB and this is
12871         // an extended SRB, convert to legacy SRB and pass to class
12872         // driver.
12873         //
12874         if ((Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
12875             ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
12876               CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
12877             ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Srb);
12878             srbPtr = &tempSrb;
12879         }
12880 
12881         retry = fdoData->InterpretSenseInfo->Interpret(Fdo,
12882                                                        OriginalRequest,
12883                                                        srbPtr,
12884                                                        MajorFunctionCode,
12885                                                        IoDeviceCode,
12886                                                        PreviousRetryCount,
12887                                                        NULL,
12888                                                        Status,
12889                                                        &tmpRetry);
12890     }
12891     else
12892     {
12893         ULONG seconds = 0;
12894 
12895         retry = ClassInterpretSenseInfo(Fdo,
12896                                         Srb,
12897                                         MajorFunctionCode,
12898                                         IoDeviceCode,
12899                                         PreviousRetryCount,
12900                                         Status,
12901                                         &seconds);
12902         tmpRetry = ((LONGLONG)seconds) * 1000 * 1000 * 10;
12903     }
12904 
12905 
12906     if (RetryIn100nsUnits != NULL)
12907     {
12908         *RetryIn100nsUnits = tmpRetry;
12909     }
12910     return retry;
12911 }
12912 
12913 VOID
12914 ClasspGetInquiryVpdSupportInfo(
12915     _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
12916     )
12917 {
12918     NTSTATUS                  status;
12919     PCOMMON_DEVICE_EXTENSION  commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
12920     SCSI_REQUEST_BLOCK        srb = {0};
12921     PCDB                      cdb;
12922     PVPD_SUPPORTED_PAGES_PAGE supportedPages = NULL;
12923     UCHAR                     bufferLength = VPD_MAX_BUFFER_SIZE;
12924     ULONG                     allocationBufferLength = bufferLength;
12925     UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
12926     PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
12927 
12928 #if defined(_ARM_) || defined(_ARM64_)
12929     //
12930     // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
12931     // based platforms. We are taking the conservative approach here.
12932     //
12933     allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
12934     supportedPages = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
12935                                            allocationBufferLength,
12936                                            '3CcS'
12937                                            );
12938 
12939 #else
12940     supportedPages = ExAllocatePoolWithTag(NonPagedPoolNx,
12941                                            bufferLength,
12942                                            '3CcS'
12943                                            );
12944 #endif
12945 
12946     if (supportedPages == NULL) {
12947         // memory allocation failure.
12948         return;
12949     }
12950 
12951     RtlZeroMemory(supportedPages, allocationBufferLength);
12952 
12953     // prepare the Srb
12954     if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
12955 
12956 #ifdef _MSC_VER
12957         #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
12958 #endif
12959         status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbExBuffer,
12960                                                STORAGE_ADDRESS_TYPE_BTL8,
12961                                                sizeof(srbExBuffer),
12962                                                1,
12963                                                SrbExDataTypeScsiCdb16);
12964 
12965         if (!NT_SUCCESS(status)) {
12966             //
12967             // Should not happen. Revert to legacy SRB.
12968             NT_ASSERT(FALSE);
12969             srb.Length = SCSI_REQUEST_BLOCK_SIZE;
12970             srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
12971             srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&srb;
12972         } else {
12973             ((PSTORAGE_REQUEST_BLOCK)srbExBuffer)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
12974             srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)srbExBuffer;
12975         }
12976 
12977     } else {
12978         srb.Length = SCSI_REQUEST_BLOCK_SIZE;
12979         srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
12980         srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&srb;
12981     }
12982 
12983     SrbSetTimeOutValue(srbHeader, FdoExtension->TimeOutValue);
12984     SrbSetRequestTag(srbHeader, SP_UNTAGGED);
12985     SrbSetRequestAttribute(srbHeader, SRB_SIMPLE_TAG_REQUEST);
12986     SrbAssignSrbFlags(srbHeader, FdoExtension->SrbFlags);
12987     SrbSetCdbLength(srbHeader, 6);
12988 
12989     cdb = SrbGetCdb(srbHeader);
12990     cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
12991     cdb->CDB6INQUIRY3.EnableVitalProductData = 1;       //EVPD bit
12992     cdb->CDB6INQUIRY3.PageCode = VPD_SUPPORTED_PAGES;
12993     cdb->CDB6INQUIRY3.AllocationLength = bufferLength;  //AllocationLength field in CDB6INQUIRY3 is only one byte.
12994 
12995     status = ClassSendSrbSynchronous(commonExtension->DeviceObject,
12996                                      (PSCSI_REQUEST_BLOCK)srbHeader,
12997                                      supportedPages,
12998                                      allocationBufferLength,
12999                                      FALSE);
13000 
13001     //
13002     // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
13003     // buffer was larger than necessary.
13004     //
13005     if (status == STATUS_DATA_OVERRUN && SrbGetDataTransferLength(srbHeader) < bufferLength)
13006     {
13007         status = STATUS_SUCCESS;
13008     }
13009 
13010     if ( NT_SUCCESS(status) &&
13011          (supportedPages->PageLength > 0) &&
13012          (supportedPages->SupportedPageList[0] > 0) ) {
13013         // ataport treats all INQUIRY command as standard INQUIRY command, thus fills invalid info
13014         // If VPD INQUIRY is supported, the first page reported (additional length field for standard INQUIRY data) should be '00'
13015         status = STATUS_NOT_SUPPORTED;
13016     }
13017 
13018     if (NT_SUCCESS(status)) {
13019         int i;
13020 
13021         for (i = 0; i < supportedPages->PageLength; i++) {
13022             if ( (i > 0) && (supportedPages->SupportedPageList[i] <= supportedPages->SupportedPageList[i - 1]) ) {
13023                 // shall be in ascending order beginning with page code 00h.
13024                 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
13025                 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits = FALSE;
13026                 FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics = FALSE;
13027                 FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning = FALSE;
13028 
13029 
13030                 break;
13031             }
13032             switch (supportedPages->SupportedPageList[i]) {
13033                 case VPD_THIRD_PARTY_COPY:
13034                     FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = TRUE;
13035                     break;
13036 
13037                 case VPD_BLOCK_LIMITS:
13038                     FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits = TRUE;
13039                     break;
13040 
13041                 case VPD_BLOCK_DEVICE_CHARACTERISTICS:
13042                     FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics = TRUE;
13043                     break;
13044 
13045                 case VPD_LOGICAL_BLOCK_PROVISIONING:
13046                     FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning = TRUE;
13047                     break;
13048 
13049             }
13050         }
13051     }
13052 
13053     ExFreePool(supportedPages);
13054 
13055     return;
13056 }
13057 
13058 //
13059 // ClasspGetLBProvisioningInfo
13060 //
13061 //  Description: This function does the work to get Logical Block Provisioning
13062 //      (LBP) info for a given LUN, including UNMAP support and parameters.  It
13063 //      will attempt to get both the Logical Block Provisioning (0xB2) VPD page
13064 //      and the Block Limits (0xB0) VPD page and cache the relevant values in
13065 //      the given FDO extension.
13066 //
13067 //      After calling this function, you can use the ClasspIsThinProvisioned()
13068 //      function to determine if the device is thinly provisioned and the
13069 //      ClasspSupportsUnmap() function to determine if the device supports the
13070 //      UNMAP command.
13071 //
13072 //  Arguments:
13073 //      - FdoExtension: The FDO extension associated with the LUN for which Thin
13074 //          Provisioning info is desired.  The Thin Provisioning info is stored
13075 //          in the FunctionSupportInfo member of this FDO extension.
13076 //
13077 // Returns:
13078 //      - STATUS_INVALID_PARAMETER if the given FDO extension has not been
13079 //          allocated properly.
13080 //      - STATUS_INSUFFICIENT_RESOURCES if this function was unable to allocate
13081 //          an SRB used to get the necessary VPD pages.
13082 //      - STATUS_SUCCESS in all other cases.  If any of the incidental functions
13083 //          don't return STATUS_SUCCESS this function will just assume Thin
13084 //          Provisioning is not enabled and return STATUS_SUCCESS.
13085 //
13086 NTSTATUS
13087 ClasspGetLBProvisioningInfo(
13088     _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
13089     )
13090 {
13091     PSCSI_REQUEST_BLOCK srb = NULL;
13092     ULONG               srbSize = 0;
13093 
13094     //
13095     // Make sure we actually have data structures to work with.
13096     //
13097     if (FdoExtension == NULL ||
13098         FdoExtension->FunctionSupportInfo == NULL) {
13099         return STATUS_INVALID_PARAMETER;
13100     }
13101 
13102     //
13103     // Allocate an SRB for querying the device for LBP-related info if either
13104     // the Logical Block Provisioning (0xB2) or Block Limits (0xB0) VPD page
13105     // exists.
13106     //
13107     if ((FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == TRUE) ||
13108         (FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits == TRUE)) {
13109 
13110         if ((FdoExtension->AdapterDescriptor != NULL) &&
13111             (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
13112             srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
13113         } else {
13114             srbSize = sizeof(SCSI_REQUEST_BLOCK);
13115         }
13116 
13117         srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, '0DcS');
13118 
13119         if (srb == NULL) {
13120             return STATUS_INSUFFICIENT_RESOURCES;
13121         }
13122     }
13123 
13124     //
13125     // Get the Logical Block Provisioning VPD page (0xB2).  This function will
13126     // set some default values if the LBP VPD page is not supported.
13127     //
13128     ClasspDeviceGetLBProvisioningVPDPage(FdoExtension->DeviceObject, srb);
13129 
13130 
13131     //
13132     // Get the Block Limits VPD page (0xB0), which may override the default
13133     // UNMAP parameter values set above.
13134     //
13135     ClasspDeviceGetBlockLimitsVPDPage(FdoExtension,
13136                                       srb,
13137                                       srbSize,
13138                                       &FdoExtension->FunctionSupportInfo->BlockLimitsData);
13139 
13140     FREE_POOL(srb);
13141 
13142     return STATUS_SUCCESS;
13143 }
13144 
13145 
13146 
13147 _IRQL_requires_(PASSIVE_LEVEL)
13148 _IRQL_requires_same_
13149 NTSTATUS
13150 ClassDetermineTokenOperationCommandSupport(
13151     _In_ PDEVICE_OBJECT DeviceObject
13152     )
13153 
13154 /*++
13155 
13156 Routine Description:
13157 
13158     This routine determines if Token Operation and Receive ROD Token Information commands
13159     are supported by this device and updates the internal structures to reflect this.
13160     In addition, it also queries the registry to determine the maximum listIdentifier
13161     to use for TokenOperation commands.
13162 
13163     This function must be called at IRQL == PASSIVE_LEVEL.
13164 
13165 Arguments:
13166 
13167     DeviceObject - The device object for which we want to determine command support.
13168 
13169 Return Value:
13170 
13171     Nothing
13172 
13173 --*/
13174 
13175 {
13176     NTSTATUS status = STATUS_SUCCESS;
13177 
13178     PAGED_CODE();
13179 
13180     TracePrint((TRACE_LEVEL_VERBOSE,
13181                 TRACE_FLAG_PNP,
13182                 "ClassDetermineTokenOperationCommandSupport (%p): Entering function.\n",
13183                 DeviceObject));
13184 
13185     //
13186     // Send down Inquiry for VPD_THIRD_PARTY_COPY_PAGE and cache away the device parameters
13187     // from WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR.
13188     //
13189     status = ClasspGetBlockDeviceTokenLimitsInfo(DeviceObject);
13190 
13191     if (NT_SUCCESS(status)) {
13192 
13193         ULONG maxListIdentifier = MaxTokenOperationListIdentifier;
13194 
13195         //
13196         // Query the maximum list identifier to use for TokenOperation commands.
13197         //
13198         if (NT_SUCCESS(ClasspGetMaximumTokenListIdentifier(DeviceObject, REG_DISK_CLASS_CONTROL, &maxListIdentifier))) {
13199             if (maxListIdentifier >= MIN_TOKEN_LIST_IDENTIFIERS) {
13200 
13201                 NT_ASSERT(maxListIdentifier <= MAX_TOKEN_LIST_IDENTIFIERS);
13202                 MaxTokenOperationListIdentifier = maxListIdentifier;
13203             }
13204         }
13205 
13206     }
13207 
13208     TracePrint((TRACE_LEVEL_VERBOSE,
13209                 TRACE_FLAG_PNP,
13210                 "ClassDetermineTokenOperationCommandSupport (%p): Exiting function with status %x.\n",
13211                 DeviceObject,
13212                 status));
13213 
13214     return status;
13215 }
13216 
13217 
13218 _IRQL_requires_same_
13219 NTSTATUS
13220 ClasspGetBlockDeviceTokenLimitsInfo(
13221     _Inout_ PDEVICE_OBJECT DeviceObject
13222     )
13223 
13224 /*++
13225 
13226 Routine Description:
13227 
13228     This routine does the work to get the Block Device Token Limits info for a given LUN.
13229     This is done by sending Inquiry for the Third Party Copy VPD page.
13230 
13231 Arguments:
13232 
13233     DeviceObject - The FDO associated with the LUN for which Block Device Token
13234                        limits info is desired.
13235 
13236 Return Value:
13237 
13238     STATUS_DEVICE_FEATURE_NOT_SUPPORTED if either the Inquiry fails or validations fail.
13239     STATUS_SUCCESS otherwise.
13240 
13241 --*/
13242 
13243 {
13244     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
13245     NTSTATUS status;
13246     PSCSI_REQUEST_BLOCK srb = NULL;
13247     ULONG srbSize = 0;
13248     USHORT pageLength = 0;
13249     USHORT descriptorLength = 0;
13250     PVOID dataBuffer = NULL;
13251     UCHAR bufferLength = VPD_MAX_BUFFER_SIZE;
13252     ULONG allocationBufferLength = bufferLength;
13253     PCDB cdb;
13254     ULONG dataTransferLength = 0;
13255     PVPD_THIRD_PARTY_COPY_PAGE operatingParameters = NULL;
13256     PWINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR blockDeviceTokenLimits = NULL;
13257 
13258     TracePrint((TRACE_LEVEL_VERBOSE,
13259                 TRACE_FLAG_PNP,
13260                 "ClasspGetBlockDeviceTokenLimitsInfo (%p): Entering function.\n",
13261                 DeviceObject));
13262 
13263     //
13264     // Allocate an SRB for querying the device for LBP-related info.
13265     //
13266     if ((fdoExtension->AdapterDescriptor != NULL) &&
13267         (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
13268         srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
13269     } else {
13270         srbSize = sizeof(SCSI_REQUEST_BLOCK);
13271     }
13272 
13273     srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, CLASSPNP_POOL_TAG_SRB);
13274 
13275     if (!srb) {
13276 
13277         TracePrint((TRACE_LEVEL_ERROR,
13278                     TRACE_FLAG_PNP,
13279                     "ClasspGetBlockDeviceTokenLimitsInfo (%p): Couldn't allocate SRB.\n",
13280                     DeviceObject));
13281 
13282         status = STATUS_INSUFFICIENT_RESOURCES;
13283         goto __ClasspGetBlockDeviceTokenLimitsInfo_Exit;
13284     }
13285 
13286 #if defined(_ARM_) || defined(_ARM64_)
13287     //
13288     // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
13289     // based platforms. We are taking the conservative approach here.
13290     //
13291     allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment());
13292     dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, CLASSPNP_POOL_TAG_VPD);
13293 #else
13294     dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CLASSPNP_POOL_TAG_VPD);
13295 #endif
13296 
13297     if (!dataBuffer) {
13298 
13299         TracePrint((TRACE_LEVEL_ERROR,
13300                     TRACE_FLAG_PNP,
13301                     "ClasspGetBlockDeviceTokenLimitsInfo (%p): Couldn't allocate dataBuffer.\n",
13302                     DeviceObject));
13303 
13304         status = STATUS_INSUFFICIENT_RESOURCES;
13305         goto __ClasspGetBlockDeviceTokenLimitsInfo_Exit;
13306     }
13307 
13308     operatingParameters = (PVPD_THIRD_PARTY_COPY_PAGE)dataBuffer;
13309 
13310     RtlZeroMemory(dataBuffer, allocationBufferLength);
13311 
13312     if ((fdoExtension->AdapterDescriptor != NULL) &&
13313         (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
13314         status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srb,
13315                                                STORAGE_ADDRESS_TYPE_BTL8,
13316                                                CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
13317                                                1,
13318                                                SrbExDataTypeScsiCdb16);
13319         if (NT_SUCCESS(status)) {
13320 
13321             ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
13322 
13323         } else {
13324 
13325             //
13326             // Should not occur. Revert to legacy SRB.
13327             //
13328             NT_ASSERT(FALSE);
13329 
13330             TracePrint((TRACE_LEVEL_WARNING,
13331                         TRACE_FLAG_PNP,
13332                         "ClasspGetBlockDeviceTokenLimitsInfo (%p): Falling back to using SRB (instead of SRB_EX).\n",
13333                         DeviceObject));
13334 
13335             RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
13336             srb->Length = sizeof(SCSI_REQUEST_BLOCK);
13337             srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
13338         }
13339     } else {
13340 
13341         RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
13342         srb->Length = sizeof(SCSI_REQUEST_BLOCK);
13343         srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
13344     }
13345 
13346     SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
13347     SrbSetRequestTag(srb, SP_UNTAGGED);
13348     SrbSetRequestAttribute(srb, SRB_SIMPLE_TAG_REQUEST);
13349     SrbAssignSrbFlags(srb, fdoExtension->SrbFlags);
13350 
13351     SrbSetCdbLength(srb, 6);
13352 
13353     cdb = SrbGetCdb(srb);
13354     cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
13355     cdb->CDB6INQUIRY3.EnableVitalProductData = 1;
13356     cdb->CDB6INQUIRY3.PageCode = VPD_THIRD_PARTY_COPY;
13357     cdb->CDB6INQUIRY3.AllocationLength = bufferLength;
13358 
13359     status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
13360                                      srb,
13361                                      dataBuffer,
13362                                      allocationBufferLength,
13363                                      FALSE);
13364 
13365     //
13366     // Handle the case where we get back STATUS_DATA_OVERRUN because the input
13367     // buffer was larger than necessary.
13368     //
13369     dataTransferLength = SrbGetDataTransferLength(srb);
13370 
13371     if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength) {
13372 
13373         status = STATUS_SUCCESS;
13374     }
13375 
13376     if (NT_SUCCESS(status)) {
13377 
13378         REVERSE_BYTES_SHORT(&pageLength, &operatingParameters->PageLength);
13379 
13380     } else {
13381         TracePrint((TRACE_LEVEL_ERROR,
13382                     TRACE_FLAG_PNP,
13383                     "ClasspGetBlockDeviceTokenLimitsInfo (%p): Inquiry for TPC VPD failed with %x.\n",
13384                     DeviceObject,
13385                     status));
13386     }
13387 
13388     if ((NT_SUCCESS(status)) &&
13389         (pageLength >= sizeof(WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR))) {
13390 
13391         USHORT descriptorType;
13392 
13393         blockDeviceTokenLimits = (PWINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR)operatingParameters->ThirdPartyCopyDescriptors;
13394         REVERSE_BYTES_SHORT(&descriptorType, &blockDeviceTokenLimits->DescriptorType);
13395         REVERSE_BYTES_SHORT(&descriptorLength, &blockDeviceTokenLimits->DescriptorLength);
13396 
13397         if ((descriptorType == BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR_TYPE_WINDOWS) &&
13398             (VPD_PAGE_HEADER_SIZE + descriptorLength == sizeof(WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR))) {
13399 
13400             fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = TRUE;
13401 
13402             REVERSE_BYTES_SHORT(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors, &blockDeviceTokenLimits->MaximumRangeDescriptors);
13403             REVERSE_BYTES(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer, &blockDeviceTokenLimits->MaximumInactivityTimer);
13404             REVERSE_BYTES(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.DefaultInactivityTimer, &blockDeviceTokenLimits->DefaultInactivityTimer);
13405             REVERSE_BYTES_QUAD(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize, &blockDeviceTokenLimits->MaximumTokenTransferSize);
13406             REVERSE_BYTES_QUAD(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount, &blockDeviceTokenLimits->OptimalTransferCount);
13407 
13408             TracePrint((TRACE_LEVEL_INFORMATION,
13409                         TRACE_FLAG_PNP,
13410                         "ClasspGetBlockDeviceTokenLimitsInfo (%p): %s %s (rev %s) reported following parameters: \
13411                         \n\t\t\tMaxRangeDescriptors: %u\n\t\t\tMaxIAT: %u\n\t\t\tDefaultIAT: %u \
13412                         \n\t\t\tMaxTokenTransferSize: %I64u\n\t\t\tOptimalTransferCount: %I64u\n\t\t\tOptimalTransferLengthGranularity: %u \
13413                         \n\t\t\tOptimalTransferLength: %u\n\t\t\tMaxTransferLength: %u\n",
13414                         DeviceObject,
13415                         (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->VendorIdOffset),
13416                         (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductIdOffset),
13417                         (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductRevisionOffset),
13418                         fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors,
13419                         fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer,
13420                         fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.DefaultInactivityTimer,
13421                         fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize,
13422                         fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount,
13423                         fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLengthGranularity,
13424                         fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLength,
13425                         fdoExtension->FunctionSupportInfo->BlockLimitsData.MaximumTransferLength));
13426         } else {
13427 
13428             TracePrint((TRACE_LEVEL_WARNING,
13429                         TRACE_FLAG_PNP,
13430                         "ClasspGetBlockDeviceTokenLimitsInfo (%p): ThirdPartyCopy VPD data doesn't have Windows OffloadDataTransfer descriptor.\n",
13431                         DeviceObject));
13432 
13433             fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
13434             status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
13435         }
13436     } else {
13437 
13438         TracePrint((TRACE_LEVEL_WARNING,
13439                     TRACE_FLAG_PNP,
13440                     "ClasspGetBlockDeviceTokenLimitsInfo (%p): TPC VPD data didn't return TPC descriptors of interest.\n",
13441                     DeviceObject));
13442 
13443         fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
13444         status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
13445     }
13446 
13447 __ClasspGetBlockDeviceTokenLimitsInfo_Exit:
13448 
13449     FREE_POOL(dataBuffer);
13450     FREE_POOL(srb);
13451     fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = status;
13452 
13453     TracePrint((TRACE_LEVEL_VERBOSE,
13454                 TRACE_FLAG_PNP,
13455                 "ClasspGetBlockDeviceTokenLimitsInfo (%p): Exiting function with status %x.\n",
13456                 DeviceObject,
13457                 status));
13458 
13459     return status;
13460 }
13461 
13462 
13463 _IRQL_requires_max_(APC_LEVEL)
13464 _IRQL_requires_min_(PASSIVE_LEVEL)
13465 _IRQL_requires_same_
13466 NTSTATUS
13467 ClassDeviceProcessOffloadRead(
13468     _In_ PDEVICE_OBJECT DeviceObject,
13469     _In_ PIRP Irp,
13470     _Inout_ PSCSI_REQUEST_BLOCK Srb
13471     )
13472 
13473 /*++
13474 
13475 Routine Description:
13476 
13477     This routine services IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES for CopyOffload
13478     Read. If the device supports copy offload, it performs the translation of the
13479     IOCTL into the appropriate SCSI commands to complete the operation.
13480 
13481     This function must be called at IRQL < DISPATCH_LEVEL.
13482 
13483 Arguments:
13484 
13485     DeviceObject - Supplies the device object associated with this request
13486     Irp - The IRP to be processed
13487     Srb - An SRB that can be optimally used to process this request
13488 
13489 Return Value:
13490 
13491     NTSTATUS code
13492 
13493 --*/
13494 
13495 {
13496     PIO_STACK_LOCATION irpStack;
13497     NTSTATUS status;
13498     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
13499     PDEVICE_DSM_OFFLOAD_READ_PARAMETERS offloadReadParameters;
13500     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
13501 
13502     UNREFERENCED_PARAMETER(Srb);
13503 
13504     //
13505     // This function must be called at less than dispatch level.
13506     // Fail if IRQL >= DISPATCH_LEVEL.
13507     //
13508     PAGED_CODE();
13509 
13510     TracePrint((TRACE_LEVEL_VERBOSE,
13511                 TRACE_FLAG_IOCTL,
13512                 "ClassDeviceProcessOffloadRead (%p): Entering function. Irp %p\n",
13513                 DeviceObject,
13514                 Irp));
13515 
13516 
13517     irpStack = IoGetCurrentIrpStackLocation (Irp);
13518 
13519     //
13520     // Validations
13521     //
13522     status = ClasspValidateOffloadSupported(DeviceObject, Irp);
13523     if (!NT_SUCCESS(status)) {
13524         goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13525     }
13526 
13527     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
13528 
13529         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
13530         TracePrint((TRACE_LEVEL_ERROR,
13531                     TRACE_FLAG_IOCTL,
13532                     "ClassDeviceProcessOffloadRead (%p): Called at raised IRQL.\n",
13533                     DeviceObject));
13534 
13535         status = STATUS_INVALID_LEVEL;
13536         goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13537     }
13538 
13539     //
13540     // Ensure that this DSM IOCTL was generated in kernel
13541     //
13542     if (Irp->RequestorMode != KernelMode) {
13543 
13544         TracePrint((TRACE_LEVEL_ERROR,
13545                     TRACE_FLAG_IOCTL,
13546                     "ClassDeviceProcessOffloadRead (%p): Called from user mode.\n",
13547                     DeviceObject));
13548 
13549         status = STATUS_ACCESS_DENIED;
13550         goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13551     }
13552 
13553     status = ClasspValidateOffloadInputParameters(DeviceObject, Irp);
13554     if (!NT_SUCCESS(status)) {
13555         goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13556     }
13557 
13558     dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
13559 
13560     //
13561     // Validate that we were passed in correct sized parameter block.
13562     //
13563     if (dsmAttributes->ParameterBlockLength < sizeof(DEVICE_DSM_OFFLOAD_READ_PARAMETERS)) {
13564 
13565         TracePrint((TRACE_LEVEL_ERROR,
13566                     TRACE_FLAG_IOCTL,
13567                     "ClassDeviceProcessOffloadRead (%p): Parameter block size (%u) too small. Required %u.\n",
13568                     DeviceObject,
13569                     dsmAttributes->ParameterBlockLength,
13570                     sizeof(DEVICE_DSM_OFFLOAD_READ_PARAMETERS)));
13571 
13572         status = STATUS_INVALID_PARAMETER;
13573         goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13574     }
13575 
13576     offloadReadParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
13577 
13578     fdoExtension = DeviceObject->DeviceExtension;
13579 
13580     //
13581     // If the request TTL is greater than the max supported by this storage, the target will
13582     // end up failing this command, so might as well do the check up front.
13583     //
13584     if ((fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer > 0) &&
13585         (offloadReadParameters->TimeToLive > fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer)) {
13586 
13587         TracePrint((TRACE_LEVEL_ERROR,
13588                     TRACE_FLAG_IOCTL,
13589                     "ClassDeviceProcessOffloadRead (%p): Requested TTL (%u) greater than max supported (%u).\n",
13590                     DeviceObject,
13591                     offloadReadParameters->TimeToLive,
13592                     fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer));
13593 
13594         status = STATUS_INVALID_PARAMETER;
13595         goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13596     }
13597 
13598     if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_OFFLOAD_READ_OUTPUT)) {
13599 
13600         TracePrint((TRACE_LEVEL_ERROR,
13601                     TRACE_FLAG_IOCTL,
13602                     "ClassDeviceProcessOffloadRead (%p): Output buffer size (%u) too small.\n",
13603                     DeviceObject,
13604                     irpStack->Parameters.DeviceIoControl.OutputBufferLength));
13605 
13606         status = STATUS_BUFFER_TOO_SMALL;
13607         goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
13608     }
13609 
13610 
13611 
13612     status = ClasspServicePopulateTokenTransferRequest(DeviceObject, Irp);
13613 
13614     if (status == STATUS_PENDING) {
13615         goto __ClassDeviceProcessOffloadRead_Exit;
13616     }
13617 
13618 __ClassDeviceProcessOffloadRead_CompleteAndExit:
13619     ClasspCompleteOffloadRequest(DeviceObject, Irp, status);
13620 __ClassDeviceProcessOffloadRead_Exit:
13621     TracePrint((TRACE_LEVEL_VERBOSE,
13622                 TRACE_FLAG_IOCTL,
13623                 "ClassDeviceProcessOffloadRead (%p): Exiting function Irp %p with status %x.\n",
13624                 DeviceObject,
13625                 Irp,
13626                 status));
13627 
13628     return status;
13629 }
13630 
13631 VOID
13632 ClasspCompleteOffloadRequest(
13633     _In_ PDEVICE_OBJECT DeviceObject,
13634     _In_ PIRP Irp,
13635     _In_ NTSTATUS CompletionStatus)
13636 {
13637     NTSTATUS status = CompletionStatus;
13638 
13639 
13640     Irp->IoStatus.Status = status;
13641 
13642     ClassReleaseRemoveLock(DeviceObject, Irp);
13643     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
13644 
13645     return;
13646 }
13647 
13648 
13649 _IRQL_requires_max_(APC_LEVEL)
13650 _IRQL_requires_min_(PASSIVE_LEVEL)
13651 _IRQL_requires_same_
13652 NTSTATUS
13653 ClassDeviceProcessOffloadWrite(
13654     _In_ PDEVICE_OBJECT DeviceObject,
13655     _In_ PIRP Irp,
13656     _Inout_ PSCSI_REQUEST_BLOCK Srb
13657     )
13658 
13659 /*++
13660 
13661 Routine Description:
13662 
13663     This routine services IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES for CopyOffload
13664     Write. If the device supports copy offload, it performs the translation of the
13665     IOCTL into the appropriate SCSI commands to complete the operation.
13666 
13667     This function must be called at IRQL < DISPATCH_LEVEL.
13668 
13669 Arguments:
13670 
13671     DeviceObject - Supplies the device object associated with this request
13672     Irp - The IRP to be processed
13673     Srb - An SRB that can be optinally used to process this request
13674 
13675 Return Value:
13676 
13677     NTSTATUS code
13678 
13679 --*/
13680 
13681 {
13682     PIO_STACK_LOCATION irpStack;
13683     NTSTATUS status;
13684     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
13685 
13686     UNREFERENCED_PARAMETER(Srb);
13687 
13688     //
13689     // This function must be called at less than dispatch level.
13690     //
13691     PAGED_CODE();
13692 
13693     TracePrint((TRACE_LEVEL_VERBOSE,
13694                 TRACE_FLAG_IOCTL,
13695                 "ClassDeviceProcessOffloadWrite (%p): Entering function. Irp %p\n",
13696                 DeviceObject,
13697                 Irp));
13698 
13699 
13700     irpStack = IoGetCurrentIrpStackLocation(Irp);
13701 
13702     //
13703     // Validations
13704     //
13705     status = ClasspValidateOffloadSupported(DeviceObject, Irp);
13706     if (!NT_SUCCESS(status)) {
13707         goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13708     }
13709 
13710     if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
13711 
13712         NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
13713         TracePrint((TRACE_LEVEL_ERROR,
13714                     TRACE_FLAG_IOCTL,
13715                     "ClassDeviceProcessOffloadWrite (%p): Called at raised IRQL.\n",
13716                     DeviceObject));
13717 
13718         status = STATUS_INVALID_LEVEL;
13719         goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13720     }
13721 
13722     //
13723     // Ensure that this DSM IOCTL was generated in kernel
13724     //
13725     if (Irp->RequestorMode != KernelMode) {
13726 
13727         TracePrint((TRACE_LEVEL_ERROR,
13728                     TRACE_FLAG_IOCTL,
13729                     "ClassDeviceProcessOffloadWrite (%p): Called from user mode.\n",
13730                     DeviceObject));
13731 
13732         status = STATUS_ACCESS_DENIED;
13733         goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13734     }
13735 
13736     status = ClasspValidateOffloadInputParameters(DeviceObject, Irp);
13737     if (!NT_SUCCESS(status)) {
13738         goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13739     }
13740 
13741     dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
13742 
13743     //
13744     // Validate that we were passed in correct sized parameter block.
13745     //
13746     if (dsmAttributes->ParameterBlockLength < sizeof(DEVICE_DSM_OFFLOAD_WRITE_PARAMETERS)) {
13747 
13748         TracePrint((TRACE_LEVEL_ERROR,
13749                     TRACE_FLAG_IOCTL,
13750                     "ClassDeviceProcessOffloadWrite (%p): Parameter block size (%u) too small. Required %u.\n",
13751                     DeviceObject,
13752                     dsmAttributes->ParameterBlockLength,
13753                     sizeof(DEVICE_DSM_OFFLOAD_WRITE_PARAMETERS)));
13754 
13755         status = STATUS_INVALID_PARAMETER;
13756         goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13757     }
13758 
13759     if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_OFFLOAD_WRITE_OUTPUT)) {
13760 
13761         TracePrint((TRACE_LEVEL_ERROR,
13762                     TRACE_FLAG_IOCTL,
13763                     "ClassDeviceProcessOffloadWrite (%p): Output buffer size (%u) too small.\n",
13764                     DeviceObject,
13765                     irpStack->Parameters.DeviceIoControl.OutputBufferLength));
13766 
13767         status = STATUS_BUFFER_TOO_SMALL;
13768         goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
13769     }
13770 
13771 
13772 
13773     status = ClasspServiceWriteUsingTokenTransferRequest(DeviceObject, Irp);
13774 
13775     if (status == STATUS_PENDING) {
13776         goto __ClassDeviceProcessOffloadWrite_Exit;
13777     }
13778 
13779 __ClassDeviceProcessOffloadWrite_CompleteAndExit:
13780     ClasspCompleteOffloadRequest(DeviceObject, Irp, status);
13781 __ClassDeviceProcessOffloadWrite_Exit:
13782     TracePrint((TRACE_LEVEL_VERBOSE,
13783                 TRACE_FLAG_IOCTL,
13784                 "ClassDeviceProcessOffloadWrite (%p): Exiting function Irp %p with status %x\n",
13785                 DeviceObject,
13786                 Irp,
13787                 status));
13788 
13789     return status;
13790 }
13791 
13792 
13793 _IRQL_requires_max_(APC_LEVEL)
13794 _IRQL_requires_min_(PASSIVE_LEVEL)
13795 _IRQL_requires_same_
13796 NTSTATUS
13797 ClasspServicePopulateTokenTransferRequest(
13798     _In_ PDEVICE_OBJECT Fdo,
13799     _In_ PIRP Irp
13800     )
13801 
13802 /*++
13803 
13804 Routine description:
13805 
13806     This routine processes offload read requests by building the SRB
13807     for PopulateToken and its result (i.e. token).
13808 
13809 Arguments:
13810 
13811     Fdo - The functional device object processing the request
13812     Irp - The Io request to be processed
13813 
13814 Return Value:
13815 
13816     STATUS_SUCCESS if successful, an error code otherwise
13817 
13818 --*/
13819 
13820 {
13821     BOOLEAN allDataSetRangeFullyConverted;
13822     UINT32 allocationSize;
13823     ULONG blockDescrIndex;
13824     PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescrPointer;
13825     PVOID buffer;
13826     ULONG bufferLength;
13827     ULONG dataSetRangeIndex;
13828     PDEVICE_DATA_SET_RANGE dataSetRanges;
13829     ULONG dataSetRangesCount;
13830     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
13831     ULONGLONG entireXferLen;
13832     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
13833     ULONG i;
13834     ULONG lbaCount;
13835     ULONG listIdentifier;
13836     ULONG maxBlockDescrCount;
13837     ULONGLONG maxLbaCount;
13838     POFFLOAD_READ_CONTEXT offloadReadContext;
13839     PDEVICE_DSM_OFFLOAD_READ_PARAMETERS offloadReadParameters;
13840     PTRANSFER_PACKET pkt;
13841     USHORT populateTokenDataLength;
13842     USHORT populateTokenDescriptorsLength;
13843     PMDL populateTokenMdl;
13844     PIRP pseudoIrp;
13845     ULONG receiveTokenInformationBufferLength;
13846     NTSTATUS status;
13847     DEVICE_DATA_SET_RANGE tempDataSetRange;
13848     BOOLEAN tempDataSetRangeFullyConverted;
13849     PUCHAR token;
13850     ULONG tokenLength;
13851     ULONG tokenOperationBufferLength;
13852     ULONGLONG totalSectorCount;
13853     ULONGLONG totalSectorsToProcess;
13854     ULONG transferSize;
13855 
13856     PAGED_CODE();
13857 
13858     TracePrint((TRACE_LEVEL_VERBOSE,
13859                 TRACE_FLAG_IOCTL,
13860                 "ClasspServicePopulateTokenTransferRequest (%p): Entering function. Irp %p\n",
13861                 Fdo,
13862                 Irp));
13863 
13864     fdoExt = Fdo->DeviceExtension;
13865     status = STATUS_SUCCESS;
13866     dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
13867     buffer = NULL;
13868     pkt = NULL;
13869     populateTokenMdl = NULL;
13870     offloadReadParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
13871     dataSetRanges = Add2Ptr(dsmAttributes, dsmAttributes->DataSetRangesOffset);
13872     dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
13873     totalSectorsToProcess = 0;
13874     token = NULL;
13875     tokenLength = 0;
13876     bufferLength = 0;
13877     offloadReadContext = NULL;
13878 
13879     NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
13880               NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
13881 
13882 
13883     for (i = 0, entireXferLen = 0; i < dataSetRangesCount; i++) {
13884         entireXferLen += dataSetRanges[i].LengthInBytes;
13885     }
13886 
13887     //
13888     // We need to truncate the read request based on the following hardware limitations:
13889     // 1. The size of the data buffer containing the TokenOperation command's parameters must
13890     //    not exceed the MaximumTransferLength (and max physical pages) of the underlying
13891     //    adapter.
13892     // 2. The number of descriptors specified in the TokenOperation command must not exceed
13893     //    the MaximumRangeDescriptors.
13894     // 3. The cumulative total of the number of transfer blocks in all the descriptors in
13895     //    the TokenOperation command must not exceed the MaximumTokenTransferSize.
13896     // 4. If the TTL has been set to the 0 (i.e. indicates the use of the default TLT),
13897     //    limit the cumulative total of the number of transfer blocks in all the descriptors
13898     //    in the TokenOperation command to the OptimalTransferCount.
13899     //    NOTE: only a TTL of 0 has an implicit indication that the initiator of the
13900     //    request would like the storage stack to be smart about the amount of data
13901     //    transfer.
13902     //
13903     // In addition to the above, we need to ensure that for each of the descriptors in the
13904     // TokenOperation command:
13905     // 1. The number of blocks specified is an exact multiple of the OptimalTransferLengthGranularity.
13906     // 2. The number of blocks specified is limited to the MaximumTransferLength. (We shall
13907     //    however, limit the number of blocks specified in each descriptor to be a maximum of
13908     //    OptimalTransferLength or MaximumTransferLength, whichever is lesser).
13909     //
13910     // Finally, we shall always send down the PopulateToken command using IMMED = 0 for this
13911     // release. This makes it simpler to handle multi-initiator scenarios since we won't need
13912     // to deal with the PopulateToken (with IMMED = 1) succeeding but ReceiveRODTokenInformation
13913     // failing due to the path/node failing, thus making it impossible to retrieve the token.
13914     //
13915     // The LBA ranges is in DEVICE_DATA_SET_RANGE format, it needs to be converted into
13916     // WINDOWS_RANGE_DESCRIPTOR Block Descriptors.
13917     //
13918 
13919     ClasspGetTokenOperationCommandBufferLength(Fdo,
13920                                                SERVICE_ACTION_POPULATE_TOKEN,
13921                                                &bufferLength,
13922                                                &tokenOperationBufferLength,
13923                                                &receiveTokenInformationBufferLength);
13924 
13925     allocationSize = sizeof(OFFLOAD_READ_CONTEXT) + bufferLength;
13926 
13927     offloadReadContext = ExAllocatePoolWithTag(
13928         NonPagedPoolNx,
13929         allocationSize,
13930         CLASSPNP_POOL_TAG_TOKEN_OPERATION);
13931 
13932     if (!offloadReadContext) {
13933 
13934         TracePrint((TRACE_LEVEL_ERROR,
13935                     TRACE_FLAG_IOCTL,
13936                     "ClasspServicePopulateTokenTransferRequest (%p): Failed to allocate buffer for PopulateToken operations.\n",
13937                     Fdo));
13938 
13939         status = STATUS_INSUFFICIENT_RESOURCES;
13940         goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
13941     }
13942 
13943     RtlZeroMemory(offloadReadContext, allocationSize);
13944 
13945     offloadReadContext->Fdo = Fdo;
13946     offloadReadContext->OffloadReadDsmIrp = Irp;
13947 
13948     //
13949     // The buffer for the commands is after the offloadReadContext.
13950     //
13951     buffer = (offloadReadContext + 1);
13952 
13953     //
13954     // No matter how large the offload read request, we'll be sending it down in one shot.
13955     // So we need only one transfer packet.
13956     //
13957     pkt = DequeueFreeTransferPacket(Fdo, TRUE);
13958     if (!pkt){
13959 
13960         TracePrint((TRACE_LEVEL_ERROR,
13961                     TRACE_FLAG_IOCTL,
13962                     "ClasspServicePopulateTokenTransferRequest (%p): Failed to retrieve transfer packet for TokenOperation (PopulateToken) operation.\n",
13963                     Fdo));
13964 
13965         status = STATUS_INSUFFICIENT_RESOURCES;
13966         goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
13967     }
13968 
13969     offloadReadContext->Pkt = pkt;
13970 
13971     ClasspGetTokenOperationDescriptorLimits(Fdo,
13972                                             SERVICE_ACTION_POPULATE_TOKEN,
13973                                             tokenOperationBufferLength,
13974                                             &maxBlockDescrCount,
13975                                             &maxLbaCount);
13976 
13977     //
13978     // We will limit the maximum data transfer in an offload read operation to:
13979     // - OTC if TTL was specified as 0
13980     // - MaximumTransferTransferSize if lesser than above, or if TTL was specified was non-zero
13981     //
13982     if ((offloadReadParameters->TimeToLive == 0) && (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount > 0)) {
13983 
13984         maxLbaCount = MIN(maxLbaCount, fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount);
13985     }
13986 
13987     //
13988     // Since we do not want very fragmented files to end up causing the PopulateToken command to take
13989     // too long (and potentially timeout), we will limit the max number of descriptors sent down in a
13990     // command.
13991     //
13992     maxBlockDescrCount = MIN(maxBlockDescrCount, MAX_NUMBER_BLOCK_DEVICE_DESCRIPTORS);
13993 
13994     TracePrint((TRACE_LEVEL_INFORMATION,
13995                 TRACE_FLAG_IOCTL,
13996                 "ClasspServicePopulateTokenTransferRequest (%p): Using MaxBlockDescrCount %u and MaxLbaCount %I64u.\n",
13997                 Fdo,
13998                 maxBlockDescrCount,
13999                 maxLbaCount));
14000 
14001     allDataSetRangeFullyConverted = FALSE;
14002     tempDataSetRangeFullyConverted = TRUE;
14003     dataSetRangeIndex = (ULONG)-1;
14004 
14005     blockDescrPointer = (PBLOCK_DEVICE_RANGE_DESCRIPTOR)
14006         &((PPOPULATE_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptor[0];
14007 
14008     RtlZeroMemory(&tempDataSetRange, sizeof(tempDataSetRange));
14009 
14010     blockDescrIndex = 0;
14011     lbaCount = 0;
14012 
14013     //
14014     // Send PopulateToken command when the buffer is full or all input entries are converted.
14015     //
14016     while (!((blockDescrIndex == maxBlockDescrCount) ||   // buffer full or block descriptor count reached
14017              (lbaCount == maxLbaCount) ||                 // block LBA count reached
14018              (allDataSetRangeFullyConverted))) {          // all DataSetRanges have been converted
14019 
14020         //
14021         // If the previous entry conversion completed, continue the next one;
14022         // Otherwise, still process the left part of the un-completed entry.
14023         //
14024         if (tempDataSetRangeFullyConverted) {
14025             dataSetRangeIndex++;
14026             tempDataSetRange.StartingOffset = dataSetRanges[dataSetRangeIndex].StartingOffset;
14027             tempDataSetRange.LengthInBytes = dataSetRanges[dataSetRangeIndex].LengthInBytes;
14028         }
14029 
14030         totalSectorCount = 0;
14031 
14032         ClasspConvertDataSetRangeToBlockDescr(Fdo,
14033                                               blockDescrPointer,
14034                                               &blockDescrIndex,
14035                                               maxBlockDescrCount,
14036                                               &lbaCount,
14037                                               maxLbaCount,
14038                                               &tempDataSetRange,
14039                                               &totalSectorCount);
14040 
14041         tempDataSetRangeFullyConverted = (tempDataSetRange.LengthInBytes == 0) ? TRUE : FALSE;
14042 
14043         allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted && ((dataSetRangeIndex + 1) == dataSetRangesCount);
14044 
14045         totalSectorsToProcess += totalSectorCount;
14046     }
14047 
14048     //
14049     // Calculate transfer size, including the header
14050     //
14051     transferSize = (blockDescrIndex * sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR)) + FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor);
14052 
14053     NT_ASSERT(transferSize <= MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH);
14054 
14055     populateTokenDataLength = (USHORT)transferSize - RTL_SIZEOF_THROUGH_FIELD(POPULATE_TOKEN_HEADER, PopulateTokenDataLength);
14056     REVERSE_BYTES_SHORT(((PPOPULATE_TOKEN_HEADER)buffer)->PopulateTokenDataLength, &populateTokenDataLength);
14057 
14058     ((PPOPULATE_TOKEN_HEADER)buffer)->Immediate = 0;
14059 
14060     REVERSE_BYTES(((PPOPULATE_TOKEN_HEADER)buffer)->InactivityTimeout, &offloadReadParameters->TimeToLive);
14061 
14062     populateTokenDescriptorsLength = (USHORT)transferSize - FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor);
14063     REVERSE_BYTES_SHORT(((PPOPULATE_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptorListLength, &populateTokenDescriptorsLength);
14064 
14065     //
14066     // Reuse a single buffer for both TokenOperation and ReceiveTokenInformation. This has the one disadvantage
14067     // that we'll be marking the page(s) as IoWriteAccess even though we only need read access for token
14068     // operation command. However, the advantage is that we eliminate the possibility of any potential failures
14069     // when trying to allocate an MDL for the ReceiveTokenInformation later on.
14070     //
14071     bufferLength = max(transferSize, receiveTokenInformationBufferLength);
14072 
14073     populateTokenMdl = ClasspBuildDeviceMdl(buffer, bufferLength, FALSE);
14074     if (!populateTokenMdl) {
14075 
14076         TracePrint((TRACE_LEVEL_ERROR,
14077                     TRACE_FLAG_IOCTL,
14078                     "ClasspServicePopulateTokenTransferRequest (%p): Failed to allocate MDL for PopulateToken operations.\n",
14079                     Fdo));
14080 
14081         status = STATUS_INSUFFICIENT_RESOURCES;
14082         goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
14083     }
14084 
14085     offloadReadContext->PopulateTokenMdl = populateTokenMdl;
14086 
14087     pseudoIrp = &offloadReadContext->PseudoIrp;
14088 
14089 
14090     pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
14091     pseudoIrp->IoStatus.Information = 0;
14092     pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
14093     pseudoIrp->MdlAddress = populateTokenMdl;
14094 
14095     InterlockedCompareExchange((volatile LONG *)&TokenOperationListIdentifier, -1, MaxTokenOperationListIdentifier);
14096     listIdentifier = InterlockedIncrement((volatile LONG *)&TokenOperationListIdentifier);
14097 
14098     ClasspSetupPopulateTokenTransferPacket(
14099         offloadReadContext,
14100         pkt,
14101         transferSize,
14102         (PUCHAR)buffer,
14103         pseudoIrp,
14104         listIdentifier);
14105 
14106     TracePrint((TRACE_LEVEL_INFORMATION,
14107                 TRACE_FLAG_IOCTL,
14108                 "ClasspServicePopulateTokenTransferRequest (%p): Generate token for %I64u bytes (versus %I64u) [via %u descriptors]. \
14109                 \n\t\t\tDataLength: %u, DescriptorsLength: %u. Pkt %p (list id %x). Requested TTL: %u secs.\n",
14110                 Fdo,
14111                 totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
14112                 entireXferLen,
14113                 blockDescrIndex,
14114                 populateTokenDataLength,
14115                 populateTokenDescriptorsLength,
14116                 pkt,
14117                 listIdentifier,
14118                 offloadReadParameters->TimeToLive));
14119 
14120     //
14121     // Save (into the offloadReadContext) any remaining things that
14122     // ClasspPopulateTokenTransferPacketDone() will need.
14123     //
14124 
14125     offloadReadContext->ListIdentifier = listIdentifier;
14126     offloadReadContext->BufferLength = bufferLength;
14127     offloadReadContext->ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength;
14128     offloadReadContext->TotalSectorsToProcess = totalSectorsToProcess; // so far
14129     offloadReadContext->EntireXferLen = entireXferLen;
14130 
14131     NT_ASSERT(status == STATUS_SUCCESS); // so far.
14132 
14133     IoMarkIrpPending(Irp);
14134     SubmitTransferPacket(pkt);
14135 
14136     status = STATUS_PENDING;
14137     goto __ClasspServicePopulateTokenTransferRequest_Exit;
14138 
14139     //
14140     // Error cleanup label only - not used in success case:
14141     //
14142 
14143 __ClasspServicePopulateTokenTransferRequest_ErrorExit:
14144 
14145     NT_ASSERT(status != STATUS_PENDING);
14146 
14147     if (offloadReadContext != NULL) {
14148         ClasspCleanupOffloadReadContext(offloadReadContext);
14149         offloadReadContext = NULL;
14150     }
14151 
14152 __ClasspServicePopulateTokenTransferRequest_Exit:
14153 
14154     TracePrint((TRACE_LEVEL_VERBOSE,
14155                 TRACE_FLAG_IOCTL,
14156                 "ClasspServicePopulateTokenTransferRequest (%p): Exiting function (Irp %p) with status %x.\n",
14157                 Fdo,
14158                 Irp,
14159                 status));
14160 
14161     return status;
14162 }
14163 
14164 
14165 VOID
14166 ClasspPopulateTokenTransferPacketDone(
14167     _In_ PVOID Context
14168     )
14169 
14170 /*++
14171 
14172 Routine description:
14173 
14174     This routine continues an offload read operation on completion of the
14175     populate token transfer packet.
14176 
14177     This function is responsible for continuing or completing the offload read
14178     operation.
14179 
14180 Arguments:
14181 
14182     Context - Pointer to the OFFLOAD_READ_CONTEXT for the offload read
14183         operation.
14184 
14185 Return Value:
14186 
14187     None.
14188 
14189 --*/
14190 
14191 {
14192     PDEVICE_OBJECT fdo;
14193     ULONG listIdentifier;
14194     POFFLOAD_READ_CONTEXT offloadReadContext;
14195     PTRANSFER_PACKET pkt;
14196     PIRP pseudoIrp;
14197     NTSTATUS status;
14198 
14199     offloadReadContext = Context;
14200     pseudoIrp = &offloadReadContext->PseudoIrp;
14201     pkt = offloadReadContext->Pkt;
14202     fdo = offloadReadContext->Fdo;
14203     listIdentifier = offloadReadContext->ListIdentifier;
14204 
14205     offloadReadContext->Pkt = NULL;
14206 
14207 
14208     status = pseudoIrp->IoStatus.Status;
14209     NT_ASSERT(status != STATUS_PENDING);
14210 
14211     if (!NT_SUCCESS(status)) {
14212         TracePrint((TRACE_LEVEL_ERROR,
14213                     TRACE_FLAG_IOCTL,
14214                     "ClasspPopulateTokenTransferPacketDone (%p): Generate token for list Id %x failed with %x (Pkt %p).\n",
14215                     fdo,
14216                     listIdentifier,
14217                     status,
14218                     pkt));
14219         goto __ClasspPopulateTokenTransferPacketDone_ErrorExit;
14220     }
14221 
14222     //
14223     // If a token was successfully generated, it is now time to retrieve the token.
14224     // The called function is responsible for completing the offload read DSM IRP.
14225     //
14226     ClasspReceivePopulateTokenInformation(offloadReadContext);
14227 
14228     //
14229     // ClasspReceivePopulateTokenInformation() takes care of completing the IRP,
14230     // so this function is done regardless of success or failure in
14231     // ClasspReceivePopulateTokenInformation().
14232     //
14233 
14234     return;
14235 
14236     //
14237     // Error cleanup label only - not used in success case:
14238     //
14239 
14240 __ClasspPopulateTokenTransferPacketDone_ErrorExit:
14241 
14242     NT_ASSERT(!NT_SUCCESS(status));
14243 
14244     //
14245     // ClasspCompleteOffloadRead also cleans up offloadReadContext.
14246     //
14247 
14248     ClasspCompleteOffloadRead(offloadReadContext, status);
14249 
14250     return;
14251 }
14252 
14253 
14254 VOID
14255 ClasspCompleteOffloadRead(
14256     _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
14257     _In_ NTSTATUS CompletionStatus
14258     )
14259 
14260 /*++
14261 
14262 Routine description:
14263 
14264     This routine completes an offload read operation with given status, and
14265     cleans up the OFFLOAD_READ_CONTEXT.
14266 
14267 Arguments:
14268 
14269     OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
14270         read operation.
14271 
14272     CompletionStatus - The completion status for the offload read operation.
14273 
14274 Return Value:
14275 
14276     None.
14277 
14278 --*/
14279 
14280 {
14281     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
14282     ULONGLONG entireXferLen;
14283     PDEVICE_OBJECT fdo;
14284     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
14285     PIRP irp;
14286     NTSTATUS status;
14287     PUCHAR token;
14288     ULONGLONG totalSectorsProcessed;
14289 
14290     status = CompletionStatus;
14291     dsmAttributes = OffloadReadContext->OffloadReadDsmIrp->AssociatedIrp.SystemBuffer;
14292     totalSectorsProcessed = OffloadReadContext->TotalSectorsProcessed;
14293     fdoExt = OffloadReadContext->Fdo->DeviceExtension;
14294     entireXferLen = OffloadReadContext->EntireXferLen;
14295     token = OffloadReadContext->Token;
14296     irp = OffloadReadContext->OffloadReadDsmIrp;
14297     fdo = OffloadReadContext->Fdo;
14298 
14299     ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->OffloadReadFlags = 0;
14300     ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->Reserved = 0;
14301 
14302     if (NT_SUCCESS(status)) {
14303         ULONGLONG totalBytesProcessed = totalSectorsProcessed * fdoExt->DiskGeometry.BytesPerSector;
14304 
14305         TracePrint((totalBytesProcessed == entireXferLen ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
14306                     TRACE_FLAG_IOCTL,
14307                     "ClasspCompleteOffloadRead (%p): Successfully populated token with %I64u (out of %I64u) bytes (list Id %x).\n",
14308                     fdo,
14309                     totalBytesProcessed,
14310                     entireXferLen,
14311                     OffloadReadContext->ListIdentifier));
14312 
14313         if (totalBytesProcessed < entireXferLen) {
14314             SET_FLAG(((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->OffloadReadFlags, STORAGE_OFFLOAD_READ_RANGE_TRUNCATED);
14315         }
14316         ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->LengthProtected = totalBytesProcessed;
14317         ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->TokenLength = STORAGE_OFFLOAD_MAX_TOKEN_LENGTH;
14318         RtlCopyMemory(&((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->Token, token, STORAGE_OFFLOAD_MAX_TOKEN_LENGTH);
14319     } else {
14320         ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->LengthProtected = 0;
14321         ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->TokenLength = 0;
14322     }
14323 
14324     irp->IoStatus.Information = sizeof(STORAGE_OFFLOAD_READ_OUTPUT);
14325 
14326     ClasspCompleteOffloadRequest(fdo, irp, status);
14327     ClasspCleanupOffloadReadContext(OffloadReadContext);
14328     OffloadReadContext = NULL;
14329 
14330     return;
14331 }
14332 
14333 
14334 VOID
14335 ClasspCleanupOffloadReadContext(
14336     _In_ __drv_freesMem(mem) POFFLOAD_READ_CONTEXT OffloadReadContext
14337     )
14338 
14339 /*++
14340 
14341 Routine description:
14342 
14343     This routine cleans up an OFFLOAD_READ_CONTEXT.
14344 
14345 Arguments:
14346 
14347     OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
14348         read operation.
14349 
14350 Return Value:
14351 
14352     None.
14353 
14354 --*/
14355 
14356 {
14357     PMDL populateTokenMdl;
14358 
14359     populateTokenMdl = OffloadReadContext->PopulateTokenMdl;
14360 
14361     NT_ASSERT(OffloadReadContext != NULL);
14362 
14363     if (populateTokenMdl) {
14364         ClasspFreeDeviceMdl(populateTokenMdl);
14365     }
14366     FREE_POOL(OffloadReadContext);
14367 
14368     return;
14369 }
14370 
14371 
14372 _IRQL_requires_same_
14373 VOID
14374 ClasspReceivePopulateTokenInformation(
14375     _In_ POFFLOAD_READ_CONTEXT OffloadReadContext
14376     )
14377 
14378 /*++
14379 
14380 Routine description:
14381 
14382     This routine retrieves the token after a PopulateToken command
14383     has been sent down.
14384 
14385     Can also be called repeatedly for a single offload op when a previous
14386     RECEIVE ROD TOKEN INFORMATION for that op indicated that the operation was
14387     not yet complete.
14388 
14389     This function is responsible for continuing or completing the offload read
14390     operation.
14391 
14392 Arguments:
14393 
14394     OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
14395         read operation.
14396 
14397 Return Value:
14398 
14399     None.
14400 
14401 --*/
14402 
14403 {
14404     PVOID buffer;
14405     ULONG bufferLength;
14406     ULONG cdbLength;
14407     PDEVICE_OBJECT fdo;
14408     PIRP irp;
14409     ULONG listIdentifier;
14410     PTRANSFER_PACKET pkt;
14411     PIRP pseudoIrp;
14412     ULONG receiveTokenInformationBufferLength;
14413     PSCSI_REQUEST_BLOCK srb;
14414     NTSTATUS status;
14415     ULONG tempSizeUlong;
14416     PULONGLONG totalSectorsProcessed;
14417 
14418     totalSectorsProcessed = &OffloadReadContext->TotalSectorsProcessed;
14419     buffer = OffloadReadContext + 1;
14420     bufferLength = OffloadReadContext->BufferLength;
14421     fdo = OffloadReadContext->Fdo;
14422     irp = OffloadReadContext->OffloadReadDsmIrp;
14423     receiveTokenInformationBufferLength = OffloadReadContext->ReceiveTokenInformationBufferLength;
14424     listIdentifier = OffloadReadContext->ListIdentifier;
14425 
14426     TracePrint((TRACE_LEVEL_VERBOSE,
14427                 TRACE_FLAG_IOCTL,
14428                 "ClasspReceivePopulateTokenInformation (%p): Entering function. Irp %p\n",
14429                 fdo,
14430                 irp));
14431 
14432     srb = &OffloadReadContext->Srb;
14433     *totalSectorsProcessed = 0;
14434 
14435     pkt = DequeueFreeTransferPacket(fdo, TRUE);
14436     if (!pkt){
14437 
14438         TracePrint((TRACE_LEVEL_ERROR,
14439                     TRACE_FLAG_IOCTL,
14440                     "ClasspReceivePopulateTokenInformation (%p): Failed to retrieve transfer packet for ReceiveTokenInformation (PopulateToken) operation.\n",
14441                     fdo));
14442 
14443         status = STATUS_INSUFFICIENT_RESOURCES;
14444         goto __ClasspReceivePopulateTokenInformation_ErrorExit;
14445     }
14446 
14447     OffloadReadContext->Pkt = pkt;
14448 
14449     RtlZeroMemory(buffer, bufferLength);
14450 
14451     tempSizeUlong = receiveTokenInformationBufferLength - 4;
14452     REVERSE_BYTES(((PRECEIVE_TOKEN_INFORMATION_HEADER)buffer)->AvailableData, &tempSizeUlong);
14453 
14454     pseudoIrp = &OffloadReadContext->PseudoIrp;
14455     RtlZeroMemory(pseudoIrp, sizeof(IRP));
14456 
14457 
14458     pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
14459     pseudoIrp->IoStatus.Information = 0;
14460     pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
14461     pseudoIrp->MdlAddress = OffloadReadContext->PopulateTokenMdl;
14462 
14463     ClasspSetupReceivePopulateTokenInformationTransferPacket(
14464         OffloadReadContext,
14465         pkt,
14466         receiveTokenInformationBufferLength,
14467         (PUCHAR)buffer,
14468         pseudoIrp,
14469         listIdentifier);
14470 
14471     //
14472     // Cache away the CDB as it may be required for forwarded sense data
14473     // after this command completes.
14474     //
14475     RtlZeroMemory(srb, sizeof(*srb));
14476     cdbLength = SrbGetCdbLength(pkt->Srb);
14477     if (cdbLength <= 16) {
14478         RtlCopyMemory(&srb->Cdb, SrbGetCdb(pkt->Srb), cdbLength);
14479     }
14480 
14481     SubmitTransferPacket(pkt);
14482 
14483     return;
14484 
14485     //
14486     // Error cleanup label only - not used in success case:
14487     //
14488 
14489 __ClasspReceivePopulateTokenInformation_ErrorExit:
14490 
14491     NT_ASSERT(!NT_SUCCESS(status));
14492 
14493     //
14494     // ClasspCompleteOffloadRead also cleans up offloadReadContext.
14495     //
14496 
14497     ClasspCompleteOffloadRead(OffloadReadContext, status);
14498 
14499     return;
14500 }
14501 
14502 
14503 VOID
14504 ClasspReceivePopulateTokenInformationTransferPacketDone(
14505     _In_ PVOID Context
14506     )
14507 
14508 /*++
14509 
14510 Routine description:
14511 
14512     This routine continues an offload read operation on completion of the
14513     RECEIVE ROD TOKEN INFORMATION transfer packet.
14514 
14515     This routine is responsible for continuing or completing the offload read
14516     operation.
14517 
14518 Arguments:
14519 
14520     Context - Pointer to the OFFLOAD_READ_CONTEXT for the offload read
14521         operation.
14522 
14523 Return Value:
14524 
14525     None.
14526 
14527 --*/
14528 
14529 {
14530     ULONG availableData;
14531     PVOID buffer;
14532     UCHAR completionStatus;
14533     ULONG estimatedRetryInterval;
14534     PDEVICE_OBJECT fdo;
14535     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
14536     PIRP irp;
14537     ULONG listIdentifier;
14538     POFFLOAD_READ_CONTEXT offloadReadContext;
14539     BOOLEAN operationCompleted;
14540     UCHAR operationStatus;
14541     PIRP pseudoIrp;
14542     USHORT segmentsProcessed;
14543     PSENSE_DATA senseData;
14544     ULONG senseDataFieldLength;
14545     UCHAR senseDataLength;
14546     PSCSI_REQUEST_BLOCK srb;
14547     NTSTATUS status;
14548     PUCHAR token;
14549     PVOID tokenAscii;
14550     PBLOCK_DEVICE_TOKEN_DESCRIPTOR tokenDescriptor;
14551     ULONG tokenDescriptorLength;
14552     PRECEIVE_TOKEN_INFORMATION_HEADER tokenInformationResults;
14553     PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER tokenInformationResultsResponse;
14554     ULONG tokenLength;
14555     ULONG tokenSize;
14556     PULONGLONG totalSectorsProcessed;
14557     ULONGLONG totalSectorsToProcess;
14558     ULONGLONG transferBlockCount;
14559 
14560     offloadReadContext = Context;
14561     fdo = offloadReadContext->Fdo;
14562     fdoExt = fdo->DeviceExtension;
14563     buffer = offloadReadContext + 1;
14564     listIdentifier = offloadReadContext->ListIdentifier;
14565     irp = offloadReadContext->OffloadReadDsmIrp;
14566     totalSectorsToProcess = offloadReadContext->TotalSectorsToProcess;
14567     totalSectorsProcessed = &offloadReadContext->TotalSectorsProcessed;
14568     srb = &offloadReadContext->Srb;
14569     tokenAscii = NULL;
14570     tokenSize = BLOCK_DEVICE_TOKEN_SIZE;
14571     tokenInformationResults = (PRECEIVE_TOKEN_INFORMATION_HEADER)buffer;
14572     senseData = (PSENSE_DATA)((PUCHAR)tokenInformationResults + FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData));
14573     transferBlockCount = 0;
14574     tokenInformationResultsResponse = NULL;
14575     tokenDescriptor = NULL;
14576     operationCompleted = FALSE;
14577     tokenDescriptorLength = 0;
14578     tokenLength = 0;
14579     token = NULL;
14580     pseudoIrp = &offloadReadContext->PseudoIrp;
14581 
14582     status = pseudoIrp->IoStatus.Status;
14583     NT_ASSERT(status != STATUS_PENDING);
14584 
14585     //
14586     // The buffer we hand allows for the max sizes for all the fields whereas the returned
14587     // data may be lesser (e.g. sense data info will almost never be MAX_SENSE_BUFFER_SIZE, etc.
14588     // so handle underrun "error"
14589     //
14590     if (status == STATUS_DATA_OVERRUN) {
14591 
14592         status = STATUS_SUCCESS;
14593     }
14594 
14595     if (!NT_SUCCESS(status)) {
14596         TracePrint((TRACE_LEVEL_ERROR,
14597                     TRACE_FLAG_IOCTL,
14598                     "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Token retrieval failed for list Id %x with %x.\n",
14599                     fdo,
14600                     listIdentifier,
14601                     status));
14602         goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
14603     }
14604 
14605     REVERSE_BYTES(&availableData, &tokenInformationResults->AvailableData);
14606 
14607     NT_ASSERT(availableData <= FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) + MAX_SENSE_BUFFER_SIZE +
14608                                FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER, TokenDescriptor) + BLOCK_DEVICE_TOKEN_SIZE);
14609 
14610     NT_ASSERT(tokenInformationResults->ResponseToServiceAction == SERVICE_ACTION_POPULATE_TOKEN);
14611 
14612     operationStatus = tokenInformationResults->OperationStatus;
14613     operationCompleted = ClasspIsTokenOperationComplete(operationStatus);
14614     NT_ASSERT(operationCompleted);
14615 
14616     REVERSE_BYTES(&estimatedRetryInterval, &tokenInformationResults->EstimatedStatusUpdateDelay);
14617 
14618     completionStatus = tokenInformationResults->CompletionStatus;
14619 
14620     NT_ASSERT(tokenInformationResults->TransferCountUnits == TRANSFER_COUNT_UNITS_NUMBER_BLOCKS);
14621     REVERSE_BYTES_QUAD(&transferBlockCount, &tokenInformationResults->TransferCount);
14622 
14623     REVERSE_BYTES_SHORT(&segmentsProcessed, &tokenInformationResults->SegmentsProcessed);
14624     NT_ASSERT(segmentsProcessed == 0);
14625 
14626     if (operationCompleted) {
14627 
14628         if (transferBlockCount > totalSectorsToProcess) {
14629 
14630             //
14631             // Buggy or hostile target.  Don't let it claim more was procesed
14632             // than was requested.  Since this is likely a bug and it's unknown
14633             // how much was actually transferred, assume no data was
14634             // transferred.
14635             //
14636 
14637             NT_ASSERT(transferBlockCount <= totalSectorsToProcess);
14638             transferBlockCount = 0;
14639         }
14640 
14641         if (operationStatus != OPERATION_COMPLETED_WITH_SUCCESS &&
14642             operationStatus != OPERATION_COMPLETED_WITH_RESIDUAL_DATA) {
14643 
14644             //
14645             // Assert on buggy response from target, but in any case, make sure not
14646             // to claim that any data was written.
14647             //
14648 
14649             NT_ASSERT(transferBlockCount == 0);
14650             transferBlockCount = 0;
14651         }
14652 
14653         //
14654         // Since the TokenOperation was sent down synchronously, the operation is complete as soon as the command returns.
14655         //
14656 
14657         senseDataFieldLength = tokenInformationResults->SenseDataFieldLength;
14658         senseDataLength = tokenInformationResults->SenseDataLength;
14659         NT_ASSERT(senseDataFieldLength >= senseDataLength);
14660 
14661         tokenInformationResultsResponse = (PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER)((PUCHAR)tokenInformationResults +
14662                                                                                                FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) +
14663                                                                                                tokenInformationResults->SenseDataFieldLength);
14664 
14665         REVERSE_BYTES(&tokenDescriptorLength, &tokenInformationResultsResponse->TokenDescriptorsLength);
14666 
14667         if (tokenDescriptorLength > 0) {
14668 
14669             NT_ASSERT(tokenDescriptorLength == sizeof(BLOCK_DEVICE_TOKEN_DESCRIPTOR));
14670 
14671             if (tokenDescriptorLength != sizeof(BLOCK_DEVICE_TOKEN_DESCRIPTOR)) {
14672 
14673                 TracePrint((TRACE_LEVEL_ERROR,
14674                             TRACE_FLAG_IOCTL,
14675                             "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Bad firmware, token descriptor length %u.\n",
14676                             fdo,
14677                             tokenDescriptorLength));
14678 
14679                 NT_ASSERT((*totalSectorsProcessed) == 0);
14680                 NT_ASSERT(tokenLength == 0);
14681 
14682             } else {
14683 
14684                 USHORT restrictedId;
14685 
14686                 tokenDescriptor = (PBLOCK_DEVICE_TOKEN_DESCRIPTOR)tokenInformationResultsResponse->TokenDescriptor;
14687 
14688                 REVERSE_BYTES_SHORT(&restrictedId, &tokenDescriptor->TokenIdentifier);
14689                 NT_ASSERT(restrictedId == 0);
14690 
14691                 tokenLength = BLOCK_DEVICE_TOKEN_SIZE;
14692                 token = tokenDescriptor->Token;
14693 
14694                 *totalSectorsProcessed = transferBlockCount;
14695 
14696                 if (transferBlockCount < totalSectorsToProcess) {
14697 
14698                     NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
14699                               operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
14700                               operationStatus == OPERATION_TERMINATED);
14701 
14702                     if (transferBlockCount == 0) {
14703                         //
14704                         // Treat the same as not getting a token.
14705                         //
14706 
14707                         tokenLength = 0;
14708                     }
14709 
14710                 } else {
14711 
14712                     NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_SUCCESS);
14713                     NT_ASSERT(transferBlockCount == totalSectorsToProcess);
14714                 }
14715 
14716                     //
14717                     // Need to convert to ascii.
14718                     //
14719                     tokenAscii = ClasspBinaryToAscii((PUCHAR)token,
14720                                                      tokenSize,
14721                                                      &tokenSize);
14722 
14723                     TracePrint((transferBlockCount == totalSectorsToProcess ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
14724                                 TRACE_FLAG_IOCTL,
14725                                 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): %wsToken %s generated successfully for list Id %x for data size %I64u bytes.\n",
14726                                 fdo,
14727                                 transferBlockCount == totalSectorsToProcess ? L"" : L"Target truncated read. ",
14728                                 (tokenAscii == NULL) ? "" : tokenAscii,
14729                                 listIdentifier,
14730                                 (*totalSectorsProcessed) * fdoExt->DiskGeometry.BytesPerSector));
14731 
14732                     FREE_POOL(tokenAscii);
14733             }
14734         } else {
14735 
14736             TracePrint((TRACE_LEVEL_ERROR,
14737                         TRACE_FLAG_IOCTL,
14738                         "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Target failed to generate a token for list Id %x for data size %I64u bytes (requested %I64u bytes).\n",
14739                         fdo,
14740                         listIdentifier,
14741                         transferBlockCount * fdoExt->DiskGeometry.BytesPerSector,
14742                         totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector));
14743 
14744             *totalSectorsProcessed = 0;
14745 
14746             NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_ERROR);
14747         }
14748 
14749         //
14750         // Operation that completes with success can have sense data (for target to pass on some extra info)
14751         // but we don't care about such sense info.
14752         // Operation that complete but not with success, may not have sense data associated, but may
14753         // have valid CompletionStatus.
14754         //
14755         // The "status" may be overriden by ClassInterpretSenseInfo().  Final
14756         // status is determined a bit later - this is just the default status
14757         // when ClassInterpretSenseInfo() doesn't get to run here.
14758         //
14759         status = STATUS_SUCCESS;
14760         if (operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
14761             operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
14762             operationStatus == OPERATION_TERMINATED) {
14763 
14764             SrbSetScsiStatus((PSTORAGE_REQUEST_BLOCK_HEADER)srb, completionStatus);
14765 
14766             if (senseDataLength) {
14767 
14768                 ULONG retryInterval;
14769 
14770                 NT_ASSERT(senseDataLength <= sizeof(SENSE_DATA));
14771 
14772                 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
14773 
14774                 srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
14775                 SrbSetSenseInfoBuffer((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseData);
14776                 SrbSetSenseInfoBufferLength((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseDataLength);
14777 
14778                 ClassInterpretSenseInfo(fdo,
14779                                         srb,
14780                                         IRP_MJ_SCSI,
14781                                         0,
14782                                         0,
14783                                         &status,
14784                                         &retryInterval);
14785 
14786                 TracePrint((TRACE_LEVEL_WARNING,
14787                             TRACE_FLAG_IOCTL,
14788                             "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Reason for truncation/failure: %x - for list Id %x for data size %I64u bytes.\n",
14789                             fdo,
14790                             status,
14791                             listIdentifier,
14792                             transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
14793             } else {
14794 
14795                 TracePrint((TRACE_LEVEL_WARNING,
14796                             TRACE_FLAG_IOCTL,
14797                             "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): No sense data available but reason for truncation/failure, possibly: %x - for list Id %x for data size %I64u bytes.\n",
14798                             fdo,
14799                             completionStatus,
14800                             listIdentifier,
14801                             transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
14802             }
14803         }
14804 
14805         if (tokenLength > 0) {
14806 
14807             offloadReadContext->Token = token;
14808 
14809             //
14810             // Even if target returned an error, from the OS upper layers' perspective,
14811             // it is a success (with truncation) if any data at all was read.
14812             //
14813             status = STATUS_SUCCESS;
14814 
14815         } else {
14816 
14817             if (NT_SUCCESS(status)) {
14818                 //
14819                 // Make sure status is a failing status, without throwing away an
14820                 // already-failing status obtained from sense data.
14821                 //
14822                 status = STATUS_UNSUCCESSFUL;
14823             }
14824         }
14825 
14826         //
14827         // Done with the operation.
14828         //
14829 
14830         NT_ASSERT(status != STATUS_PENDING);
14831         goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
14832 
14833     } else {
14834 
14835         status = STATUS_UNSUCCESSFUL;
14836 
14837         TracePrint((TRACE_LEVEL_ERROR,
14838                     TRACE_FLAG_IOCTL,
14839                     "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Token retrieval failed for list Id %x with %x.\n",
14840                     fdo,
14841                     listIdentifier,
14842                     status));
14843 
14844         NT_ASSERT(*totalSectorsProcessed == 0);
14845         goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
14846     }
14847 
14848 __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit:
14849 
14850     if (status != STATUS_PENDING) {
14851 
14852         //
14853         // The "status" value can be success or failure at this point, as
14854         // appropriate.
14855         //
14856 
14857         ClasspCompleteOffloadRead(offloadReadContext, status);
14858     }
14859 
14860     //
14861     // Due to tracing a potentially freed pointer value "Irp", this trace could
14862     // be delayed beyond another offload op picking up the same pointer value
14863     // for its Irp.  This function exits after the operation is complete when
14864     // status != STATUS_PENDING.
14865     //
14866 
14867     TracePrint((TRACE_LEVEL_VERBOSE,
14868                 TRACE_FLAG_IOCTL,
14869                 "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Exiting function (Irp %p) with internal status %x.\n",
14870                 fdo,
14871                 irp,
14872                 status));
14873 
14874     return;
14875 }
14876 
14877 
14878 _IRQL_requires_max_(APC_LEVEL)
14879 _IRQL_requires_min_(PASSIVE_LEVEL)
14880 _IRQL_requires_same_
14881 NTSTATUS
14882 ClasspServiceWriteUsingTokenTransferRequest(
14883     _In_ PDEVICE_OBJECT Fdo,
14884     _In_ PIRP Irp
14885     )
14886 
14887 /*++
14888 
14889 Routine description:
14890 
14891     This routine processes offload write requests by building the SRB
14892     for WriteUsingToken.
14893 
14894 Arguments:
14895 
14896     Fdo - The functional device object processing the request
14897     Irp - The Io request to be processed
14898 
14899 Return Value:
14900 
14901     STATUS_SUCCESS if successful, an error code otherwise
14902 
14903 --*/
14904 
14905 {
14906     ULONG allocationSize;
14907     PVOID buffer;
14908     ULONG bufferLength;
14909     PDEVICE_DATA_SET_RANGE dataSetRanges;
14910     ULONG dataSetRangesCount;
14911     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
14912     ULONGLONG entireXferLen;
14913     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
14914     ULONG i;
14915     ULONGLONG logicalBlockOffset;
14916     ULONG maxBlockDescrCount;
14917     ULONGLONG maxLbaCount;
14918     POFFLOAD_WRITE_CONTEXT offloadWriteContext;
14919     PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS offloadWriteParameters;
14920     ULONG receiveTokenInformationBufferLength;
14921     NTSTATUS status;
14922     NTSTATUS tempStatus;
14923     BOOLEAN tokenInvalidated;
14924     ULONG tokenOperationBufferLength;
14925     PMDL writeUsingTokenMdl;
14926 
14927     PAGED_CODE();
14928 
14929     TracePrint((TRACE_LEVEL_VERBOSE,
14930                 TRACE_FLAG_IOCTL,
14931                 "ClasspServiceWriteUsingTokenTransferRequest (%p): Entering function. Irp %p.\n",
14932                 Fdo,
14933                 Irp));
14934 
14935     fdoExt = Fdo->DeviceExtension;
14936     status = STATUS_SUCCESS;
14937     tempStatus = STATUS_SUCCESS;
14938     dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
14939     buffer = NULL;
14940     writeUsingTokenMdl = NULL;
14941     offloadWriteParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
14942     dataSetRanges = Add2Ptr(dsmAttributes, dsmAttributes->DataSetRangesOffset);
14943     dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
14944     logicalBlockOffset = offloadWriteParameters->TokenOffset / fdoExt->DiskGeometry.BytesPerSector;
14945     tokenInvalidated = FALSE;
14946     bufferLength = 0;
14947 
14948 
14949     NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
14950               NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
14951 
14952     for (i = 0, entireXferLen = 0; i < dataSetRangesCount; i++) {
14953         entireXferLen += dataSetRanges[i].LengthInBytes;
14954     }
14955 
14956     //
14957     // We need to split the write request based on the following hardware limitations:
14958     // 1. The size of the data buffer containing the TokenOperation command's parameters must
14959     //    not exceed the MaximumTransferLength (and max physical pages) of the underlying
14960     //    adapter.
14961     // 2. The number of descriptors specified in the TokenOperation command must not exceed
14962     //    the MaximumRangeDescriptors.
14963     // 3. The cumulative total of the number of transfer blocks in all the descriptors in
14964     //    the TokenOperation command must not exceed the MaximumTokenTransferSize.
14965     //
14966     // In addition to the above, we need to ensure that for each of the descriptors in the
14967     // TokenOperation command:
14968     // 1. The number of blocks specified is an exact multiple of the OptimalTransferLengthGranularity.
14969     // 2. The number of blocks specified is limited to the MaximumTransferLength. (We shall
14970     //    however, limit the number of blocks specified in each descriptor to be a maximum of
14971     //    OptimalTransferLength or MaximumTransferLength, whichever is lesser).
14972     //
14973     // Finally, we shall always send down the WriteUsingToken command using IMMED = 0 for this
14974     // release. This makes it simpler to handle multi-initiator scenarios since we won't need
14975     // to deal with the WriteUsingToken (with IMMED = 1) succeeding but ReceiveRODTokenInformation
14976     // failing due to the path/node failing, thus making it impossible to retrieve results of
14977     // the data transfer operation, or to cancel the data transfer via CopyOperationAbort.
14978     // Since a write data transfer of a large amount of data may take a long time when sent
14979     // down IMMED = 0, we shall limit the size to a maximum of 256MB even if it means truncating
14980     // the original requested size to this capped size. The application is expected to deal with
14981     // this truncation.
14982     // (NOTE: the cap of 256MB is chosen to match with the Copy Engine's chunk size).
14983     //
14984     // The LBA ranges is in DEVICE_DATA_SET_RANGE format, it needs to be converted into
14985     // WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR Block Descriptors.
14986     //
14987 
14988     ClasspGetTokenOperationCommandBufferLength(Fdo,
14989                                                SERVICE_ACTION_WRITE_USING_TOKEN,
14990                                                &bufferLength,
14991                                                &tokenOperationBufferLength,
14992                                                &receiveTokenInformationBufferLength);
14993 
14994     allocationSize = sizeof(OFFLOAD_WRITE_CONTEXT) + bufferLength;
14995 
14996     offloadWriteContext = ExAllocatePoolWithTag(
14997         NonPagedPoolNx,
14998         allocationSize,
14999         CLASSPNP_POOL_TAG_TOKEN_OPERATION);
15000 
15001     if (!offloadWriteContext) {
15002 
15003         TracePrint((TRACE_LEVEL_ERROR,
15004                     TRACE_FLAG_IOCTL,
15005                     "ClasspServiceWriteUsingTokenTransferRequest (%p): Failed to allocate buffer for WriteUsingToken operations.\n",
15006                     Fdo));
15007 
15008         status = STATUS_INSUFFICIENT_RESOURCES;
15009         goto __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit;
15010     }
15011 
15012     //
15013     // Only zero the context portion here.  The buffer portion is zeroed for
15014     // each sub-request.
15015     //
15016     RtlZeroMemory(offloadWriteContext, sizeof(OFFLOAD_WRITE_CONTEXT));
15017 
15018     offloadWriteContext->Fdo = Fdo;
15019     offloadWriteContext->OffloadWriteDsmIrp = Irp;
15020     offloadWriteContext->OperationStartTime = KeQueryInterruptTime();
15021 
15022     //
15023     // The buffer for the commands is after the offloadWriteContext.
15024     //
15025     buffer = (offloadWriteContext + 1);
15026 
15027     //
15028     // Set up fields that allow iterating through whole request, by issuing sub-
15029     // requests which each do some of the writing.  Because of truncation by the
15030     // target, it's not known exactly how many bytes will be written by the
15031     // target in each sub-request (can be less than requested), so the
15032     // progress through the outer request must only commit the move through the
15033     // upper DSM ranges when a lower request is done and the number of written
15034     // sectors is known.
15035     //
15036 
15037     NT_ASSERT(offloadWriteContext->TotalSectorsProcessedSuccessfully == 0);
15038     offloadWriteContext->TotalRequestSizeSectors = entireXferLen / fdoExt->DiskGeometry.BytesPerSector;
15039     NT_ASSERT(offloadWriteContext->DataSetRangeIndex == 0);
15040     NT_ASSERT(offloadWriteContext->DataSetRangeByteOffset == 0);
15041     offloadWriteContext->DataSetRangesCount = dataSetRangesCount;
15042 
15043     offloadWriteContext->DsmAttributes = dsmAttributes;
15044     offloadWriteContext->OffloadWriteParameters = offloadWriteParameters;
15045     offloadWriteContext->DataSetRanges = dataSetRanges;
15046     offloadWriteContext->LogicalBlockOffset = logicalBlockOffset;
15047 
15048     ClasspGetTokenOperationDescriptorLimits(Fdo,
15049                                             SERVICE_ACTION_WRITE_USING_TOKEN,
15050                                             tokenOperationBufferLength,
15051                                             &maxBlockDescrCount,
15052                                             &maxLbaCount);
15053 
15054     if (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount && fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize) {
15055 
15056         NT_ASSERT(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount <= fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize);
15057     }
15058 
15059     //
15060     // We will limit the maximum data transfer in an offload write operation to:
15061     // - 64MB if OptimalTransferCount = 0
15062     // - OptimalTransferCount if < 256MB
15063     // - 256MB if OptimalTransferCount >= 256MB
15064     // - MaximumTokenTransferSize if lesser than above chosen size
15065     //
15066     if (0 == fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount) {
15067 
15068         maxLbaCount = MIN(maxLbaCount, (DEFAULT_MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector));
15069 
15070     } else if (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount < (MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector)) {
15071 
15072         maxLbaCount = MIN(maxLbaCount, fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount);
15073 
15074     } else {
15075 
15076         maxLbaCount = MIN(maxLbaCount, (MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector));
15077     }
15078 
15079     //
15080     // Since we do not want very fragmented files to end up causing the WriteUsingToken command to take
15081     // too long (and potentially timeout), we will limit the max number of descriptors sent down in a
15082     // command.
15083     //
15084     maxBlockDescrCount = MIN(maxBlockDescrCount, MAX_NUMBER_BLOCK_DEVICE_DESCRIPTORS);
15085 
15086     TracePrint((TRACE_LEVEL_INFORMATION,
15087                 TRACE_FLAG_IOCTL,
15088                 "ClasspServiceWriteUsingTokenTransferRequest (%p): Using MaxBlockDescrCount %u and MaxLbaCount %I64u.\n",
15089                 Fdo,
15090                 maxBlockDescrCount,
15091                 maxLbaCount));
15092 
15093     offloadWriteContext->MaxBlockDescrCount = maxBlockDescrCount;
15094     offloadWriteContext->MaxLbaCount = maxLbaCount;
15095 
15096     //
15097     // Reuse a single buffer for both TokenOperation and ReceiveTokenInformation. This has the one disadvantage
15098     // that we'll be marking the page(s) as IoWriteAccess even though we only need read access for token
15099     // operation command and we may not even need to send down a ReceiveTokenInformation (in case of a successful
15100     // synchronous transfer). However, the advantage is that we eliminate the possibility of any potential
15101     // failures when trying to allocate an MDL for the ReceiveTokenInformation later on if we need to send it.
15102     //
15103     writeUsingTokenMdl = ClasspBuildDeviceMdl(buffer, bufferLength, FALSE);
15104     if (!writeUsingTokenMdl) {
15105 
15106         TracePrint((TRACE_LEVEL_ERROR,
15107                     TRACE_FLAG_IOCTL,
15108                     "ClasspServiceWriteUsingTokenTransferRequest (%p): Failed to allocate MDL for WriteUsingToken operations.\n",
15109                     Fdo));
15110 
15111         status = STATUS_INSUFFICIENT_RESOURCES;
15112         goto __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit;
15113     }
15114 
15115     offloadWriteContext->WriteUsingTokenMdl = writeUsingTokenMdl;
15116 
15117     //
15118     // There are potentially two approaches that we can take:
15119     // 1. Determine how many transfer packets we need (in case we need to split the request), get
15120     //    them all up-front and then send down all the split WriteUsingToken commands in parallel.
15121     //    The benefit of this approach is that the performance will be improved in the success case.
15122     //    But error case handling becomes very complex to handle, since if one of the intermediate
15123     //    write fails, there is no way to cancel the remaining writes that were sent. Waiting for
15124     //    such requests to complete in geo-distributed source and target cases can be very time
15125     //    consuming. The complexity gets worse in the case that the target succeeds only a partial
15126     //    amount of data for one the of intermediate split commands.
15127     //                                       [OR]
15128     // 2. Until the entire data set range is processed, build the command for as much of the range as
15129     //    possible, send down a packet, and once it completes, repeat sequentially in a loop.
15130     //    The advantage of this approach is its simplistic nature. In the success case, it will
15131     //    be less performant as compared to the previous approach, but since the gain of offload
15132     //    copy is so significant compared to native buffered-copy, the tradeoff is acceptable.
15133     //    In the failure case the simplicity offers the following benefit - if any command fails,
15134     //    there is no further processing that is needed. And the cumulative total bytes that succeeded
15135     //    (until the failing split WriteUsingToken command) is easily tracked.
15136     //
15137     // Given the above, we're going with the second approach.
15138     //
15139 
15140     NT_ASSERT(status == STATUS_SUCCESS); // so far
15141 
15142     //
15143     // Save (into the offloadReadContext) any remaining things that
15144     // ClasspPopulateTokenTransferPacketDone() will need.
15145     //
15146 
15147     offloadWriteContext->BufferLength = bufferLength;
15148     offloadWriteContext->ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength;
15149     offloadWriteContext->EntireXferLen = entireXferLen;
15150 
15151     IoMarkIrpPending(Irp);
15152     ClasspContinueOffloadWrite(offloadWriteContext);
15153 
15154     status = STATUS_PENDING;
15155     goto __ClasspServiceWriteUsingTokenTransferRequest_Exit;
15156 
15157     //
15158     // Error label only - not used in success case:
15159     //
15160 
15161 __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit:
15162 
15163     NT_ASSERT(status != STATUS_PENDING);
15164 
15165     if (offloadWriteContext != NULL) {
15166         ClasspCleanupOffloadWriteContext(offloadWriteContext);
15167         offloadWriteContext = NULL;
15168     }
15169 
15170 __ClasspServiceWriteUsingTokenTransferRequest_Exit:
15171 
15172     TracePrint((TRACE_LEVEL_VERBOSE,
15173                 TRACE_FLAG_IOCTL,
15174                 "ClasspServiceWriteUsingTokenTransferRequest (%p): Exiting function (Irp %p) with status %x.\n",
15175                 Fdo,
15176                 Irp,
15177                 status));
15178 
15179     return status;
15180 }
15181 
15182 
15183 VOID
15184 #ifdef _MSC_VER
15185 #pragma warning(suppress: 28194) // This function will either alias or free OffloadWriteContext
15186 #endif
15187 ClasspContinueOffloadWrite(
15188     _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext
15189     )
15190 
15191 /*++
15192 
15193 Routine description:
15194 
15195     This routine continues an offload write operation.  This routine expects the
15196     offload write operation to be set up and ready to start a WRITE USING TOKEN,
15197     but with no WRITE USING TOKEN currently in flight.
15198 
15199     This routine is responsible for continuing or completing the offload write
15200     operation.
15201 
15202 Arguments:
15203 
15204     OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15205         write operation.
15206 
15207 Return Value:
15208 
15209     None.
15210 
15211 --*/
15212 
15213 {
15214     BOOLEAN allDataSetRangeFullyConverted;
15215     ULONG blockDescrIndex;
15216     PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescrPointer;
15217     PVOID buffer;
15218     ULONG bufferLength;
15219     ULONGLONG dataSetRangeByteOffset;
15220     ULONG dataSetRangeIndex;
15221     PDEVICE_DATA_SET_RANGE dataSetRanges;
15222     ULONG dataSetRangesCount;
15223     ULONGLONG entireXferLen;
15224     PDEVICE_OBJECT fdo;
15225     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
15226     PIRP irp;
15227     ULONG lbaCount;
15228     ULONG listIdentifier;
15229     ULONGLONG logicalBlockOffset;
15230     ULONG maxBlockDescrCount;
15231     ULONGLONG maxLbaCount;
15232     PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS offloadWriteParameters;
15233     PTRANSFER_PACKET pkt;
15234     PIRP pseudoIrp;
15235     NTSTATUS status;
15236     DEVICE_DATA_SET_RANGE tempDataSetRange;
15237     BOOLEAN tempDataSetRangeFullyConverted;
15238     PVOID tokenAscii;
15239     ULONG tokenSize;
15240     ULONGLONG totalSectorCount;
15241     ULONGLONG totalSectorsProcessedSuccessfully;
15242     ULONGLONG totalSectorsToProcess;
15243     ULONG transferSize;
15244     USHORT writeUsingTokenDataLength;
15245     USHORT writeUsingTokenDescriptorsLength;
15246     PMDL writeUsingTokenMdl;
15247 
15248     tokenAscii = NULL;
15249     tokenSize = BLOCK_DEVICE_TOKEN_SIZE;
15250     tempDataSetRangeFullyConverted = FALSE;
15251     allDataSetRangeFullyConverted = FALSE;
15252     fdo = OffloadWriteContext->Fdo;
15253     fdoExt = fdo->DeviceExtension;
15254     irp = OffloadWriteContext->OffloadWriteDsmIrp;
15255     buffer = OffloadWriteContext + 1;
15256     bufferLength = OffloadWriteContext->BufferLength;
15257     dataSetRanges = OffloadWriteContext->DataSetRanges;
15258     offloadWriteParameters = OffloadWriteContext->OffloadWriteParameters;
15259     pseudoIrp = &OffloadWriteContext->PseudoIrp;
15260     writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
15261     entireXferLen = OffloadWriteContext->EntireXferLen;
15262 
15263     RtlZeroMemory(buffer, bufferLength);
15264 
15265     pkt = DequeueFreeTransferPacket(fdo, TRUE);
15266     if (!pkt){
15267 
15268         TracePrint((TRACE_LEVEL_ERROR,
15269                     TRACE_FLAG_IOCTL,
15270                     "ClasspContinueOffloadWrite (%p): Failed to retrieve transfer packet for TokenOperation (WriteUsingToken) operation.\n",
15271                     fdo));
15272 
15273         status = STATUS_INSUFFICIENT_RESOURCES;
15274         goto __ClasspContinueOffloadWrite_ErrorExit;
15275     }
15276 
15277     OffloadWriteContext->Pkt = pkt;
15278 
15279     blockDescrPointer = (PBLOCK_DEVICE_RANGE_DESCRIPTOR)
15280         &((PWRITE_USING_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptor[0];
15281 
15282     blockDescrIndex = 0;
15283     lbaCount = 0;
15284 
15285     totalSectorsToProcess = 0;
15286 
15287     maxBlockDescrCount = OffloadWriteContext->MaxBlockDescrCount;
15288     maxLbaCount = OffloadWriteContext->MaxLbaCount;
15289 
15290     //
15291     // The OffloadWriteContext->DataSetRangeIndex, DataSetRangeByteOffset, and
15292     // TotalSectorsProcessedSuccessfully don't move forward until RRTI has
15293     // reported the actual amount written.
15294     //
15295     // For that reason, this function only updates
15296     // OffloadWriteContext->TotalSectorsProcessed, which tracks the number of
15297     // sectors requested to be written by the current WRITE USING TOKEN command
15298     // (not all will necessarily be written).
15299     //
15300 
15301     dataSetRangeIndex = OffloadWriteContext->DataSetRangeIndex;
15302     dataSetRangesCount = OffloadWriteContext->DataSetRangesCount;
15303     dataSetRangeByteOffset = OffloadWriteContext->DataSetRangeByteOffset;
15304     totalSectorsProcessedSuccessfully = OffloadWriteContext->TotalSectorsProcessedSuccessfully;
15305 
15306     //
15307     // Send WriteUsingToken commands when the buffer is full or all input entries are converted.
15308     //
15309     while (!((blockDescrIndex == maxBlockDescrCount) ||        // buffer full or block descriptor count reached
15310              (lbaCount == maxLbaCount) ||                      // block LBA count reached
15311              (allDataSetRangeFullyConverted))) {               // all DataSetRanges have been converted
15312 
15313         NT_ASSERT(dataSetRangeIndex < dataSetRangesCount);
15314         NT_ASSERT(dataSetRangeByteOffset < dataSetRanges[dataSetRangeIndex].LengthInBytes);
15315 
15316         tempDataSetRange.StartingOffset = dataSetRanges[dataSetRangeIndex].StartingOffset + dataSetRangeByteOffset;
15317         tempDataSetRange.LengthInBytes = dataSetRanges[dataSetRangeIndex].LengthInBytes - dataSetRangeByteOffset;
15318 
15319         totalSectorCount = 0;
15320 
15321         ClasspConvertDataSetRangeToBlockDescr(fdo,
15322                                               blockDescrPointer,
15323                                               &blockDescrIndex,
15324                                               maxBlockDescrCount,
15325                                               &lbaCount,
15326                                               maxLbaCount,
15327                                               &tempDataSetRange,
15328                                               &totalSectorCount);
15329 
15330         tempDataSetRangeFullyConverted = (tempDataSetRange.LengthInBytes == 0) ? TRUE : FALSE;
15331 
15332         allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted && ((dataSetRangeIndex + 1) == dataSetRangesCount);
15333 
15334         if (tempDataSetRangeFullyConverted) {
15335             dataSetRangeIndex += 1;
15336             dataSetRangeByteOffset = 0;
15337             NT_ASSERT(dataSetRangeIndex <= dataSetRangesCount);
15338         } else {
15339             dataSetRangeByteOffset += totalSectorCount * fdoExt->DiskGeometry.BytesPerSector;
15340             NT_ASSERT(dataSetRangeByteOffset < dataSetRanges[dataSetRangeIndex].LengthInBytes);
15341         }
15342 
15343         totalSectorsToProcess += totalSectorCount;
15344     }
15345 
15346     //
15347     // Save the number of sectors being attempted in this WRITE USING TOKEN
15348     // command, so that a success return from the command will know how much
15349     // was written, without needing to issue a RECEIVE ROD TOKEN INFORMATION
15350     // command.
15351     //
15352     OffloadWriteContext->TotalSectorsToProcess = totalSectorsToProcess;
15353     OffloadWriteContext->TotalSectorsProcessed = 0;
15354 
15355     //
15356     // Calculate transfer size, including the header
15357     //
15358     transferSize = (blockDescrIndex * sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR)) + FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
15359 
15360     NT_ASSERT(transferSize <= MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH);
15361 
15362     writeUsingTokenDataLength = (USHORT)transferSize - RTL_SIZEOF_THROUGH_FIELD(WRITE_USING_TOKEN_HEADER, WriteUsingTokenDataLength);
15363     REVERSE_BYTES_SHORT(((PWRITE_USING_TOKEN_HEADER)buffer)->WriteUsingTokenDataLength, &writeUsingTokenDataLength);
15364 
15365     ((PWRITE_USING_TOKEN_HEADER)buffer)->Immediate = 0;
15366 
15367     logicalBlockOffset = OffloadWriteContext->LogicalBlockOffset + totalSectorsProcessedSuccessfully;
15368     REVERSE_BYTES_QUAD(((PWRITE_USING_TOKEN_HEADER)buffer)->BlockOffsetIntoToken, &logicalBlockOffset);
15369 
15370     RtlCopyMemory(((PWRITE_USING_TOKEN_HEADER)buffer)->Token,
15371                   &offloadWriteParameters->Token,
15372                   BLOCK_DEVICE_TOKEN_SIZE);
15373 
15374     writeUsingTokenDescriptorsLength = (USHORT)transferSize - FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
15375     REVERSE_BYTES_SHORT(((PWRITE_USING_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptorListLength, &writeUsingTokenDescriptorsLength);
15376 
15377     RtlZeroMemory(pseudoIrp, sizeof(IRP));
15378 
15379     pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
15380     pseudoIrp->IoStatus.Information = 0;
15381     pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
15382     pseudoIrp->MdlAddress = writeUsingTokenMdl;
15383 
15384     InterlockedCompareExchange((volatile LONG *)&TokenOperationListIdentifier, -1, MaxTokenOperationListIdentifier);
15385     listIdentifier = InterlockedIncrement((volatile LONG *)&TokenOperationListIdentifier);
15386 
15387     ClasspSetupWriteUsingTokenTransferPacket(
15388         OffloadWriteContext,
15389         pkt,
15390         transferSize,
15391         (PUCHAR)buffer,
15392         pseudoIrp,
15393         listIdentifier);
15394 
15395     tokenAscii = ClasspBinaryToAscii((PUCHAR)(((PWRITE_USING_TOKEN_HEADER)buffer)->Token),
15396                                      tokenSize,
15397                                      &tokenSize);
15398 
15399     TracePrint((TRACE_LEVEL_INFORMATION,
15400                 TRACE_FLAG_IOCTL,
15401                 "ClasspContinueOffloadWrite (%p): Offloading write for %I64u bytes (versus %I64u) [via %u descriptors]. \
15402                 \n\t\t\tDataLength: %u, DescriptorsLength: %u. Pkt %p (list id %x) [Token: %s]\n",
15403                 fdo,
15404                 totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
15405                 entireXferLen,
15406                 blockDescrIndex,
15407                 writeUsingTokenDataLength,
15408                 writeUsingTokenDescriptorsLength,
15409                 pkt,
15410                 listIdentifier,
15411                 (tokenAscii == NULL) ? "" : tokenAscii));
15412 
15413     FREE_POOL(tokenAscii);
15414 
15415     OffloadWriteContext->ListIdentifier = listIdentifier;
15416 
15417     SubmitTransferPacket(pkt);
15418 
15419     //
15420     // ClasspWriteUsingTokenTransferPacketDone() takes care of completing the
15421     // IRP, so this function is done.
15422     //
15423 
15424     return;
15425 
15426     //
15427     // Error cleaup label only - not used in success case:
15428     //
15429 
15430 __ClasspContinueOffloadWrite_ErrorExit:
15431 
15432     NT_ASSERT(!NT_SUCCESS(status));
15433 
15434     //
15435     // ClasspCompleteOffloadWrite also cleans up offloadWriteContext.
15436     //
15437 
15438     ClasspCompleteOffloadWrite(OffloadWriteContext, status);
15439 
15440     return;
15441 }
15442 
15443 
15444 VOID
15445 ClasspAdvanceOffloadWritePosition(
15446     _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
15447     _In_ ULONGLONG SectorsToAdvance
15448     )
15449 
15450 /*++
15451 
15452 Routine description:
15453 
15454     After the target has responded to WRITE USING TOKEN with success, or RRTI
15455     with a specific TRANSFER COUNT, this routine is used to update the relative
15456     position within the overall offload write request.  This position includes
15457     the TotalSectorsProcessedSuccessfully, the DataSetRangeIndex, and the
15458     DataSetRangeByteOffset.
15459 
15460     The caller is responsible for continuing or completing the offload write
15461     operation (this routine doesn't do that).
15462 
15463 Arguments:
15464 
15465     OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15466         write operation.
15467 
15468     SectorsToAdvance - The number of sectors which were just written
15469         successfully (not the total for the offload write operation overall,
15470         just the number done by the most recent WRITE USING TOKEN).
15471 
15472 Return Value:
15473 
15474     None.
15475 
15476 --*/
15477 
15478 {
15479     ULONGLONG bytesToAdvance;
15480     ULONGLONG bytesToDo;
15481     PULONGLONG dataSetRangeByteOffset;
15482     PULONG dataSetRangeIndex;
15483     PDEVICE_DATA_SET_RANGE dataSetRanges;
15484     PDEVICE_OBJECT fdo;
15485     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
15486     PULONGLONG totalSectorsProcessedSuccessfully;
15487 
15488     fdo = OffloadWriteContext->Fdo;
15489     fdoExt = fdo->DeviceExtension;
15490     dataSetRanges = OffloadWriteContext->DataSetRanges;
15491     dataSetRangeByteOffset = &OffloadWriteContext->DataSetRangeByteOffset;
15492     dataSetRangeIndex = &OffloadWriteContext->DataSetRangeIndex;
15493     totalSectorsProcessedSuccessfully = &OffloadWriteContext->TotalSectorsProcessedSuccessfully;
15494     bytesToAdvance = SectorsToAdvance * fdoExt->DiskGeometry.BytesPerSector;
15495 
15496     (*totalSectorsProcessedSuccessfully) += SectorsToAdvance;
15497     NT_ASSERT((*totalSectorsProcessedSuccessfully) <= OffloadWriteContext->TotalRequestSizeSectors);
15498 
15499     while (bytesToAdvance != 0) {
15500         bytesToDo = dataSetRanges[*dataSetRangeIndex].LengthInBytes - *dataSetRangeByteOffset;
15501         if (bytesToDo > bytesToAdvance) {
15502             bytesToDo = bytesToAdvance;
15503         }
15504         (*dataSetRangeByteOffset) += bytesToDo;
15505         bytesToAdvance -= bytesToDo;
15506         if ((*dataSetRangeByteOffset) == dataSetRanges[*dataSetRangeIndex].LengthInBytes) {
15507             (*dataSetRangeIndex) += 1;
15508             (*dataSetRangeByteOffset) = 0;
15509         }
15510     }
15511 
15512     NT_ASSERT((*dataSetRangeIndex) <= OffloadWriteContext->DataSetRangesCount);
15513 
15514     return;
15515 }
15516 
15517 
15518 VOID
15519 ClasspWriteUsingTokenTransferPacketDone(
15520     _In_ PVOID Context
15521     )
15522 
15523 /*++
15524 
15525 Routine description:
15526 
15527     This routine continues an offload write operation when the WRITE USING
15528     TOKEN transfer packet completes.
15529 
15530     This routine may be able to determine that all requested sectors were
15531     written if the WRITE USING TOKEN completed with success, or may need to
15532     issue a RECEIVE ROD TOKEN INFORMATION if the WRITE USING TOKEN indicated
15533     check condition.
15534 
15535     This routine is responsible for continuing or completing the offload write
15536     operation.
15537 
15538 Arguments:
15539 
15540     Context - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload write
15541         operation.
15542 
15543 Return Value:
15544 
15545     None.
15546 
15547 --*/
15548 
15549 {
15550     ULONGLONG entireXferLen;
15551     PDEVICE_OBJECT fdo;
15552     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
15553     ULONG listIdentifier;
15554     POFFLOAD_WRITE_CONTEXT offloadWriteContext;
15555     PTRANSFER_PACKET pkt;
15556     PIRP pseudoIrp;
15557     NTSTATUS status;
15558     PBOOLEAN tokenInvalidated;
15559     ULONGLONG totalSectorsToProcess;
15560 
15561     offloadWriteContext = Context;
15562     pseudoIrp = &offloadWriteContext->PseudoIrp;
15563     fdo = offloadWriteContext->Fdo;
15564     fdoExt = fdo->DeviceExtension;
15565     listIdentifier = offloadWriteContext->ListIdentifier;
15566     totalSectorsToProcess = offloadWriteContext->TotalSectorsToProcess;
15567     entireXferLen = offloadWriteContext->EntireXferLen;
15568     tokenInvalidated = &offloadWriteContext->TokenInvalidated;
15569     pkt = offloadWriteContext->Pkt;
15570 
15571     offloadWriteContext->Pkt = NULL;
15572 
15573 
15574     status = pseudoIrp->IoStatus.Status;
15575     NT_ASSERT(status != STATUS_PENDING);
15576 
15577     //
15578     // If the request failed with any of the following errors, then it is meaningless to send
15579     // down a ReceiveTokenInformation (regardless of whether the transfer was requested as sync
15580     // or async), since the target has no saved information about the command:
15581     // - STATUS_INVALID_TOKEN
15582     // - STATUS_INVALID_PARAMETER
15583     //
15584     if (status == STATUS_INVALID_PARAMETER ||
15585         status == STATUS_INVALID_TOKEN) {
15586 
15587         TracePrint((TRACE_LEVEL_ERROR,
15588                     TRACE_FLAG_IOCTL,
15589                     "ClasspWriteUsingTokenTransferPacketDone (%p): Write failed with %x (list id %x).\n",
15590                     fdo,
15591                     status,
15592                     listIdentifier));
15593 
15594         //
15595         // If the token isn't valid any longer, we should let the upper layers know so that
15596         // they don't waste time retrying the write with the same token.
15597         //
15598         if (status == STATUS_INVALID_TOKEN) {
15599 
15600             *tokenInvalidated = TRUE;
15601         }
15602 
15603         NT_ASSERT(status != STATUS_PENDING && !NT_SUCCESS(status));
15604         goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
15605 
15606     } else if ((NT_SUCCESS(status)) &&
15607                (pkt->Srb->SrbStatus == SRB_STATUS_SUCCESS || pkt->TransferCount != 0)) {
15608 
15609         //
15610         // If the TokenOperation command was sent to the target requesting synchronous data
15611         // transfer, a success indicates that the command is complete.
15612         // This could either be because of a successful completion of the entire transfer
15613         // or because of a partial transfer due to target truncation. If it is the latter,
15614         // and the information field of the sense data has returned the TransferCount, we
15615         // can avoid sending down an RRTI.
15616         //
15617         if (pkt->Srb->SrbStatus == SRB_STATUS_SUCCESS) {
15618 
15619             //
15620             // The entire transfer has completed successfully.
15621             //
15622             offloadWriteContext->TotalSectorsProcessed = totalSectorsToProcess;
15623             TracePrint((TRACE_LEVEL_INFORMATION,
15624                         TRACE_FLAG_IOCTL,
15625                         "ClasspWriteUsingTokenTransferPacketDone (%p): Successfully wrote using token %I64u (out of %I64u) bytes (list Id %x).\n",
15626                         fdo,
15627                         totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
15628                         entireXferLen,
15629                         listIdentifier));
15630         } else {
15631 
15632             //
15633             // The target has returned how much data it transferred in the response to the
15634             // WUT command itself, allowing us to optimize by removing the necessaity for
15635             // sending down an RRTI to query the TransferCount.
15636             //
15637             NT_ASSERT(pkt->TransferCount);
15638 
15639             offloadWriteContext->TotalSectorsProcessed = totalSectorsToProcess = pkt->TransferCount;
15640             TracePrint((TRACE_LEVEL_INFORMATION,
15641                         TRACE_FLAG_IOCTL,
15642                         "ClasspWriteUsingTokenTransferPacketDone (%p): Target truncated write using token %I64u (out of %I64u) bytes (list Id %x).\n",
15643                         fdo,
15644                         totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
15645                         entireXferLen,
15646                         listIdentifier));
15647         }
15648 
15649         ClasspAdvanceOffloadWritePosition(offloadWriteContext, totalSectorsToProcess);
15650 
15651         NT_ASSERT(status != STATUS_PENDING);
15652 
15653         //
15654         // ClasspReceiveWriteUsingTokenInformationDone() takes care of
15655         // completing the operation (eventually), so pending from point of view
15656         // of this function.
15657         //
15658 
15659         ClasspReceiveWriteUsingTokenInformationDone(offloadWriteContext, status);
15660         status = STATUS_PENDING;
15661 
15662         goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
15663 
15664     } else {
15665 
15666         //
15667         // Since the TokenOperation was failed (or the target truncated the transfer but
15668         // didn't indicate the amount), we need to send down ReceiveTokenInformation.
15669         //
15670         TracePrint((TRACE_LEVEL_ERROR,
15671                     TRACE_FLAG_IOCTL,
15672                     "ClasspWriteUsingTokenTransferPacketDone (%p): Write failed with status %x, %x (list id %x).\n",
15673                     fdo,
15674                     status,
15675                     pkt->Srb->SrbStatus,
15676                     listIdentifier));
15677 
15678         ClasspReceiveWriteUsingTokenInformation(offloadWriteContext);
15679 
15680         status = STATUS_PENDING;
15681         goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
15682     }
15683 
15684 __ClasspWriteUsingTokenTransferPacketDone_Exit:
15685 
15686     if (status != STATUS_PENDING) {
15687         ClasspCompleteOffloadWrite(offloadWriteContext, status);
15688     }
15689 
15690     return;
15691 }
15692 
15693 
15694 VOID
15695 ClasspReceiveWriteUsingTokenInformationDone(
15696     _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
15697     _In_ NTSTATUS CompletionCausingStatus
15698     )
15699 
15700 /*++
15701 
15702 Routine description:
15703 
15704     This routine continues an offload write operation when a WRITE USING TOKEN
15705     and possible associated RECEIVE ROD TOKEN INFORMATION have both fully
15706     completed and the RRTI has indicated completion of the WUT.
15707 
15708     This routine checks to see if the total sectors written is already equal to
15709     the overall total requested sector count, and if so, completes the offload
15710     write operation.  If not, this routine continues the operation by issuing
15711     another WUT.
15712 
15713     This routine is responsible for continuing or completing the offload write
15714     operation.
15715 
15716 Arguments:
15717 
15718     OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15719         write operation.
15720 
15721 Return Value:
15722 
15723     None.
15724 
15725 --*/
15726 
15727 {
15728     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = OffloadWriteContext->Fdo->DeviceExtension;
15729     PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
15730 
15731     //
15732     // Time taken in 100ns units.
15733     //
15734     ULONGLONG durationIn100ns = (KeQueryInterruptTime() - OffloadWriteContext->OperationStartTime);
15735     ULONGLONG maxTargetDuration = fdoData->CopyOffloadMaxTargetDuration * 10ULL * 1000 * 1000;
15736 
15737     NT_ASSERT(
15738         OffloadWriteContext->TotalSectorsProcessedSuccessfully <=
15739         OffloadWriteContext->TotalRequestSizeSectors);
15740 
15741 
15742     if (OffloadWriteContext->TotalSectorsProcessedSuccessfully == OffloadWriteContext->TotalRequestSizeSectors) {
15743 
15744         ClasspCompleteOffloadWrite(OffloadWriteContext, CompletionCausingStatus);
15745 
15746         goto __ClasspReceiveWriteUsingTokenInformationDone_Exit;
15747     }
15748 
15749     //
15750     // Since we don't want a layered timeout mechanism (e.g. guest and parent OS in Hyper-V scenarios)
15751     // to cause a SCSI timeout for the higher layer token operations.
15752     //
15753     if (maxTargetDuration <= durationIn100ns) {
15754 
15755         TracePrint((TRACE_LEVEL_WARNING,
15756                     TRACE_FLAG_IOCTL,
15757                     "ClasspReceiveWriteUsingTokenInformationDone (%p): Truncating write (list id %x) because of max-duration-rule.\n",
15758                     OffloadWriteContext->Fdo,
15759                     OffloadWriteContext->ListIdentifier));
15760 
15761         //
15762         // We could technically pass in STATUS_IO_OPERATION_TIMEOUT, but ClasspCompleteOffloadWrite
15763         // won't end up doing anything (useful) with this status, since some bytes would already
15764         // have been transferred.
15765         //
15766         ClasspCompleteOffloadWrite(OffloadWriteContext, STATUS_UNSUCCESSFUL);
15767 
15768         goto __ClasspReceiveWriteUsingTokenInformationDone_Exit;
15769     }
15770 
15771     NT_ASSERT(
15772         OffloadWriteContext->TotalSectorsProcessedSuccessfully <
15773         OffloadWriteContext->TotalRequestSizeSectors);
15774 
15775     //
15776     // Keep going with the next sub-request.
15777     //
15778 
15779     ClasspContinueOffloadWrite(OffloadWriteContext);
15780 
15781 __ClasspReceiveWriteUsingTokenInformationDone_Exit:
15782 
15783     return;
15784 }
15785 
15786 
15787 VOID
15788 ClasspCompleteOffloadWrite(
15789     _In_ __drv_freesMem(Mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
15790     _In_ NTSTATUS CompletionCausingStatus
15791     )
15792 
15793 /*++
15794 
15795 Routine description:
15796 
15797     This routine is used to complete an offload write operation.
15798 
15799     The input CompletionCausingStatus doesn't necessarily drive the completion
15800     status of the offload write operation overall, if the offload write
15801     operation overall has previously written some sectors successfully.
15802 
15803     This routine completes the offload write operation.
15804 
15805 Arguments:
15806 
15807     OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15808         write operation.
15809 
15810     CompletionCausingStatus - Status code indicating a reason that the offload
15811         write operation is completing.  For success this will be STATUS_SUCCESS,
15812         but for a failure, this status will indicate what failure occurred.
15813         This status doesn't directly propagate to the completion status of the
15814         overall offload write operation if this status is failure and the
15815         overall offload write operation has already previously written some
15816         sectors successfully.
15817 
15818 Return Value:
15819 
15820     None.
15821 
15822 --*/
15823 
15824 {
15825     PDEVICE_OBJECT fdo;
15826     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
15827     PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
15828     PULONGLONG totalSectorsProcessedSuccessfully;
15829     ULONGLONG entireXferLen;
15830     PIRP irp;
15831     PBOOLEAN tokenInvalidated;
15832     ULONG listIdentifier;
15833     ULONGLONG totalSectorsProcessed;
15834     NTSTATUS status;
15835     ULONGLONG totalBytesProcessed;
15836 
15837     fdo = OffloadWriteContext->Fdo;
15838     fdoExt = fdo->DeviceExtension;
15839     dsmAttributes = OffloadWriteContext->DsmAttributes;
15840     totalSectorsProcessedSuccessfully = &OffloadWriteContext->TotalSectorsProcessedSuccessfully;
15841     entireXferLen = OffloadWriteContext->EntireXferLen;
15842     irp = OffloadWriteContext->OffloadWriteDsmIrp;
15843     tokenInvalidated = &OffloadWriteContext->TokenInvalidated;
15844     listIdentifier = OffloadWriteContext->ListIdentifier;
15845     totalSectorsProcessed = OffloadWriteContext->TotalSectorsProcessed;
15846     status = CompletionCausingStatus;
15847 
15848     ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags = 0;
15849     ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->Reserved = 0;
15850 
15851     totalBytesProcessed = (*totalSectorsProcessedSuccessfully) * fdoExt->DiskGeometry.BytesPerSector;
15852 
15853     TracePrint((totalBytesProcessed == entireXferLen ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
15854                 TRACE_FLAG_IOCTL,
15855                 "ClasspCompleteOffloadWrite (%p): %ws wrote using token %I64u (out of %I64u) bytes (Irp %p).\n",
15856                 fdo,
15857                 NT_SUCCESS(status) ? L"Successful" : L"Failed",
15858                 totalBytesProcessed,
15859                 entireXferLen,
15860                 irp));
15861 
15862     if (totalBytesProcessed > 0 && totalBytesProcessed < entireXferLen) {
15863         SET_FLAG(((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags, STORAGE_OFFLOAD_WRITE_RANGE_TRUNCATED);
15864     }
15865     if (*tokenInvalidated) {
15866         SET_FLAG(((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags, STORAGE_OFFLOAD_TOKEN_INVALID);
15867     }
15868     ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->LengthCopied = totalBytesProcessed;
15869 
15870 
15871     if (!NT_SUCCESS(status)) {
15872 
15873         TracePrint((TRACE_LEVEL_WARNING,
15874                     TRACE_FLAG_IOCTL,
15875                     "ClasspCompleteOffloadWrite (%p): TokenOperation for WriteUsingToken (list Id %u) completed with %x writing %I64u blocks (currentTotal %I64u blocks).\n",
15876                     fdo,
15877                     listIdentifier,
15878                     status,
15879                     totalSectorsProcessed,
15880                     *totalSectorsProcessedSuccessfully));
15881 
15882         //
15883         // Even if target returned an error, from the OS upper layers' perspective,
15884         // it is a success (with truncation) if any data at all was written.
15885         //
15886         if (*totalSectorsProcessedSuccessfully) {
15887             status = STATUS_SUCCESS;
15888         }
15889     }
15890 
15891     irp->IoStatus.Information = sizeof(STORAGE_OFFLOAD_WRITE_OUTPUT);
15892 
15893     ClasspCompleteOffloadRequest(fdo, irp, status);
15894     ClasspCleanupOffloadWriteContext(OffloadWriteContext);
15895     OffloadWriteContext = NULL;
15896 
15897     return;
15898 }
15899 
15900 
15901 VOID
15902 ClasspCleanupOffloadWriteContext(
15903     _In_ __drv_freesMem(mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext
15904     )
15905 
15906 /*++
15907 
15908 Routine description:
15909 
15910     This routine cleans up an offload write context.
15911 
15912 Arguments:
15913 
15914     OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15915         write operation.
15916 
15917 Return Value:
15918 
15919     None.
15920 
15921 --*/
15922 
15923 {
15924     PMDL writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
15925 
15926     if (writeUsingTokenMdl) {
15927         ClasspFreeDeviceMdl(writeUsingTokenMdl);
15928     }
15929     FREE_POOL(OffloadWriteContext);
15930 
15931     return;
15932 }
15933 
15934 
15935 _IRQL_requires_same_
15936 VOID
15937 ClasspReceiveWriteUsingTokenInformation(
15938     _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
15939     )
15940 
15941 /*++
15942 
15943 Routine description:
15944 
15945     This routine retrieves the token after a WriteUsingToken command
15946     has been sent down in case of an error or if there is a need to
15947     poll for the result.
15948 
15949     This routine is responsible for continuing or completing the offload write
15950     operation.
15951 
15952 Arguments:
15953 
15954     OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
15955         write operation.
15956 
15957 Return Value:
15958 
15959     None.
15960 
15961 --*/
15962 
15963 {
15964     PVOID buffer;
15965     ULONG bufferLength;
15966     ULONG cdbLength;
15967     PDEVICE_OBJECT fdo;
15968     PIRP irp;
15969     ULONG listIdentifier;
15970     PTRANSFER_PACKET pkt;
15971     PIRP pseudoIrp;
15972     ULONG receiveTokenInformationBufferLength;
15973     PSCSI_REQUEST_BLOCK srb;
15974     NTSTATUS status;
15975     ULONG tempSizeUlong;
15976     PMDL writeUsingTokenMdl;
15977 
15978     fdo = OffloadWriteContext->Fdo;
15979     irp = OffloadWriteContext->OffloadWriteDsmIrp;
15980     pseudoIrp = &OffloadWriteContext->PseudoIrp;
15981     buffer = OffloadWriteContext + 1;
15982     bufferLength = OffloadWriteContext->BufferLength;
15983     receiveTokenInformationBufferLength = OffloadWriteContext->ReceiveTokenInformationBufferLength;
15984     writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
15985     listIdentifier = OffloadWriteContext->ListIdentifier;
15986     srb = &OffloadWriteContext->Srb;
15987     status = STATUS_SUCCESS;
15988 
15989     TracePrint((TRACE_LEVEL_VERBOSE,
15990                 TRACE_FLAG_IOCTL,
15991                 "ClasspReceiveWriteUsingTokenInformation (%p): Entering function. Irp %p\n",
15992                 fdo,
15993                 irp));
15994 
15995     //
15996     // The WRITE USING TOKEN wasn't immediately fully successful, so that means
15997     // the only way to find out how many sectors were processed by the WRITE
15998     // USING TOKEN is to get a successful RECEIVE ROD TOKEN INFORMATION that
15999     // indicates the operation is complete.
16000     //
16001 
16002     NT_ASSERT(OffloadWriteContext->TotalSectorsProcessed == 0);
16003 
16004     pkt = DequeueFreeTransferPacket(fdo, TRUE);
16005 
16006     if (!pkt) {
16007 
16008         TracePrint((TRACE_LEVEL_ERROR,
16009                     TRACE_FLAG_IOCTL,
16010                     "ClasspReceiveWriteUsingTokenInformation (%p): Failed to retrieve transfer packet for ReceiveTokenInformation (WriteUsingToken) operation.\n",
16011                     fdo));
16012 
16013         status = STATUS_INSUFFICIENT_RESOURCES;
16014 
16015         goto __ClasspReceiveWriteUsingTokenInformation_ErrorExit;
16016     }
16017 
16018     RtlZeroMemory(buffer, bufferLength);
16019 
16020     tempSizeUlong = receiveTokenInformationBufferLength - 4;
16021     REVERSE_BYTES(((PRECEIVE_TOKEN_INFORMATION_HEADER)buffer)->AvailableData, &tempSizeUlong);
16022 
16023     RtlZeroMemory(pseudoIrp, sizeof(IRP));
16024 
16025     pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
16026     pseudoIrp->IoStatus.Information = 0;
16027     pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
16028     pseudoIrp->MdlAddress = writeUsingTokenMdl;
16029 
16030     ClasspSetupReceiveWriteUsingTokenInformationTransferPacket(
16031         OffloadWriteContext,
16032         pkt,
16033         bufferLength,
16034         (PUCHAR)buffer,
16035         pseudoIrp,
16036         listIdentifier);
16037 
16038     //
16039     // Cache away the CDB as it may be required for forwarded sense data
16040     // after this command completes.
16041     //
16042     RtlZeroMemory(srb, sizeof(*srb));
16043     cdbLength = SrbGetCdbLength(pkt->Srb);
16044     if (cdbLength <= 16) {
16045         RtlCopyMemory(&srb->Cdb, SrbGetCdb(pkt->Srb), cdbLength);
16046     }
16047 
16048     SubmitTransferPacket(pkt);
16049 
16050     return;
16051 
16052     //
16053     // Error label only - not used by success cases:
16054     //
16055 
16056 __ClasspReceiveWriteUsingTokenInformation_ErrorExit:
16057 
16058     NT_ASSERT(!NT_SUCCESS(status));
16059 
16060     //
16061     // ClasspCompleteOffloadWrite also cleans up OffloadWriteContext.
16062     //
16063 
16064     ClasspCompleteOffloadWrite(OffloadWriteContext, status);
16065 
16066     return;
16067 }
16068 
16069 
16070 VOID
16071 ClasspReceiveWriteUsingTokenInformationTransferPacketDone(
16072     _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
16073     )
16074 
16075 /*++
16076 
16077 Routine description:
16078 
16079     This routine continues an offload write operation when a RECEIVE ROD TOKEN
16080     INFORMATION transfer packet is done.
16081 
16082     This routine may need to send another RRTI, or it may be able to indicate
16083     that this WUT is done via call to
16084     ClasspReceiveWriteUsingTokenInformationDone().
16085 
16086     This routine is responsible for continuing or completing the offload write
16087     operation.
16088 
16089 Arguments:
16090 
16091     OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
16092         write operation.
16093 
16094 Return Value:
16095 
16096     None.
16097 
16098 --*/
16099 
16100 {
16101     ULONG availableData;
16102     PVOID buffer;
16103     UCHAR completionStatus;
16104     ULONG estimatedRetryInterval;
16105     PDEVICE_OBJECT fdo;
16106     PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
16107     PIRP irp;
16108     ULONG listIdentifier;
16109     BOOLEAN operationCompleted;
16110     UCHAR operationStatus;
16111     PIRP pseudoIrp;
16112     USHORT segmentsProcessed;
16113     PSENSE_DATA senseData;
16114     ULONG senseDataFieldLength;
16115     UCHAR senseDataLength;
16116     PSCSI_REQUEST_BLOCK srb;
16117     NTSTATUS status;
16118     ULONG tokenDescriptorLength;
16119     PRECEIVE_TOKEN_INFORMATION_HEADER tokenInformationResults;
16120     PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER tokenInformationResponsePadding;
16121     PBOOLEAN tokenInvalidated;
16122     PULONGLONG totalSectorsProcessed;
16123     ULONGLONG totalSectorsToProcess;
16124     ULONGLONG transferBlockCount;
16125 
16126     fdo = OffloadWriteContext->Fdo;
16127     fdoExt = fdo->DeviceExtension;
16128     listIdentifier = OffloadWriteContext->ListIdentifier;
16129     totalSectorsProcessed = &OffloadWriteContext->TotalSectorsProcessed;
16130     totalSectorsToProcess = OffloadWriteContext->TotalSectorsToProcess;
16131     irp = OffloadWriteContext->OffloadWriteDsmIrp;
16132     pseudoIrp = &OffloadWriteContext->PseudoIrp;
16133     tokenInvalidated = &OffloadWriteContext->TokenInvalidated;
16134     srb = &OffloadWriteContext->Srb;
16135     operationCompleted = FALSE;
16136     buffer = OffloadWriteContext + 1;
16137     tokenInformationResults = (PRECEIVE_TOKEN_INFORMATION_HEADER)buffer;
16138     senseData = (PSENSE_DATA)((PUCHAR)tokenInformationResults + FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData));
16139     transferBlockCount = 0;
16140     tokenInformationResponsePadding = NULL;
16141     tokenDescriptorLength = 0;
16142 
16143     NT_ASSERT((*totalSectorsProcessed) == 0);
16144 
16145     OffloadWriteContext->Pkt = NULL;
16146 
16147 
16148     status = pseudoIrp->IoStatus.Status;
16149     NT_ASSERT(status != STATUS_PENDING);
16150 
16151     //
16152     // The buffer we hand allows for the max sizes for all the fields whereas the returned
16153     // data may be lesser (e.g. sense data info will almost never be MAX_SENSE_BUFFER_SIZE, etc.
16154     // so handle underrun "error"
16155     //
16156     if (status == STATUS_DATA_OVERRUN) {
16157 
16158         status = STATUS_SUCCESS;
16159     }
16160 
16161     if (!NT_SUCCESS(status)) {
16162         TracePrint((TRACE_LEVEL_ERROR,
16163                     TRACE_FLAG_IOCTL,
16164                     "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Failed with %x to retrieve write results for list Id %x for data size %I64u bytes.\n",
16165                     fdo,
16166                     status,
16167                     listIdentifier,
16168                     totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector));
16169 
16170         NT_ASSERT((*totalSectorsProcessed) == 0);
16171 
16172         goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
16173     }
16174 
16175     REVERSE_BYTES(&availableData, &tokenInformationResults->AvailableData);
16176 
16177     NT_ASSERT(availableData <= FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) + MAX_SENSE_BUFFER_SIZE);
16178 
16179     NT_ASSERT(tokenInformationResults->ResponseToServiceAction == SERVICE_ACTION_WRITE_USING_TOKEN);
16180 
16181     operationStatus = tokenInformationResults->OperationStatus;
16182     operationCompleted = ClasspIsTokenOperationComplete(operationStatus);
16183     NT_ASSERT(operationCompleted);
16184 
16185     REVERSE_BYTES(&estimatedRetryInterval, &tokenInformationResults->EstimatedStatusUpdateDelay);
16186 
16187     completionStatus = tokenInformationResults->CompletionStatus;
16188 
16189     senseDataFieldLength = tokenInformationResults->SenseDataFieldLength;
16190     senseDataLength = tokenInformationResults->SenseDataLength;
16191     NT_ASSERT(senseDataFieldLength >= senseDataLength);
16192 
16193     tokenInformationResponsePadding = (PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER)((PUCHAR)tokenInformationResults +
16194                                                                                            FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) +
16195                                                                                            tokenInformationResults->SenseDataFieldLength);
16196 
16197     REVERSE_BYTES(&tokenDescriptorLength, &tokenInformationResponsePadding->TokenDescriptorsLength);
16198     NT_ASSERT(tokenDescriptorLength == 0);
16199 
16200     NT_ASSERT(tokenInformationResults->TransferCountUnits == TRANSFER_COUNT_UNITS_NUMBER_BLOCKS);
16201     REVERSE_BYTES_QUAD(&transferBlockCount, &tokenInformationResults->TransferCount);
16202 
16203     REVERSE_BYTES_SHORT(&segmentsProcessed, &tokenInformationResults->SegmentsProcessed);
16204     NT_ASSERT(segmentsProcessed == 0);
16205 
16206     if (operationCompleted) {
16207 
16208         if (transferBlockCount > totalSectorsToProcess) {
16209 
16210             //
16211             // Buggy or hostile target.  Don't let it claim more was procesed
16212             // than was requested.  Since this is likely a bug and it's unknown
16213             // how much was actually transferred, assume no data was
16214             // transferred.
16215             //
16216 
16217             NT_ASSERT(transferBlockCount <= totalSectorsToProcess);
16218             transferBlockCount = 0;
16219         }
16220 
16221         if (operationStatus != OPERATION_COMPLETED_WITH_SUCCESS &&
16222             operationStatus != OPERATION_COMPLETED_WITH_RESIDUAL_DATA) {
16223 
16224             //
16225             // Assert on buggy response from target, but in any case, make sure not
16226             // to claim that any data was written.
16227             //
16228 
16229             NT_ASSERT(transferBlockCount == 0);
16230             transferBlockCount = 0;
16231         }
16232 
16233         //
16234         // Since the TokenOperation was sent down synchronously but failed, the operation is complete as soon as the
16235         // ReceiveTokenInformation command returns.
16236         //
16237 
16238         NT_ASSERT((*totalSectorsProcessed) == 0);
16239         *totalSectorsProcessed = transferBlockCount;
16240         ClasspAdvanceOffloadWritePosition(OffloadWriteContext, transferBlockCount);
16241 
16242         if (transferBlockCount < totalSectorsToProcess) {
16243 
16244             NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
16245                       operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
16246                       operationStatus == OPERATION_TERMINATED);
16247 
16248         } else {
16249 
16250             NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_SUCCESS);
16251         }
16252 
16253         TracePrint((transferBlockCount == totalSectorsToProcess ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
16254                     TRACE_FLAG_IOCTL,
16255                     "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): %wsSuccessfully wrote (for list Id %x) for data size %I64u bytes\n",
16256                     fdo,
16257                     transferBlockCount == totalSectorsToProcess ? L"" : L"Target truncated write. ",
16258                     listIdentifier,
16259                     (*totalSectorsProcessed) * fdoExt->DiskGeometry.BytesPerSector));
16260 
16261         //
16262         // Operation that completes with success can have sense data (for target to pass on some extra info)
16263         // but we don't care about such sense info.
16264         // Operation that complete but not with success, may not have sense data associated, but may
16265         // have valid CompletionStatus.
16266         //
16267         // The "status" may be overriden by ClassInterpretSenseInfo().  Final
16268         // status is determined a bit later - this is just the default status
16269         // when ClassInterpretSenseInfo() doesn't get to run here.
16270         //
16271         status = STATUS_SUCCESS;
16272         if (operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
16273             operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
16274             operationStatus == OPERATION_TERMINATED) {
16275 
16276             SrbSetScsiStatus((PSTORAGE_REQUEST_BLOCK_HEADER)srb, completionStatus);
16277 
16278             if (senseDataLength) {
16279 
16280                 ULONG retryInterval;
16281 
16282                 NT_ASSERT(senseDataLength <= sizeof(SENSE_DATA));
16283 
16284                 srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
16285 
16286                 srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
16287                 SrbSetSenseInfoBuffer((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseData);
16288                 SrbSetSenseInfoBufferLength((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseDataLength);
16289 
16290                 ClassInterpretSenseInfo(fdo,
16291                                         srb,
16292                                         IRP_MJ_SCSI,
16293                                         0,
16294                                         0,
16295                                         &status,
16296                                         &retryInterval);
16297 
16298                 TracePrint((TRACE_LEVEL_WARNING,
16299                             TRACE_FLAG_IOCTL,
16300                             "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Reason for truncation/failure: %x - for list Id %x for data size %I64u bytes.\n",
16301                             fdo,
16302                             status,
16303                             listIdentifier,
16304                             transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
16305 
16306                 //
16307                 // If the token isn't valid any longer, we should let the upper layers know so that
16308                 // they don't waste time retrying the write with the same token.
16309                 //
16310                 if (status == STATUS_INVALID_TOKEN) {
16311 
16312                     *tokenInvalidated = TRUE;
16313                 }
16314             } else {
16315 
16316                 TracePrint((TRACE_LEVEL_WARNING,
16317                             TRACE_FLAG_IOCTL,
16318                             "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): No sense data available but reason for truncation/failure, possibly: %x - for list Id %x for data size %I64u bytes.\n",
16319                             fdo,
16320                             completionStatus,
16321                             listIdentifier,
16322                             transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
16323             }
16324         }
16325 
16326         //
16327         // Initialize status. Upper layer needs to know if command failed because it
16328         // timed out without doing any writing.  ClasspCompleteOffloadWrite() will
16329         // force status to success if any data was written, so it's this function's
16330         // job to set the status appropriately based on the outcome of this
16331         // WRITE USING TOKEN command, and then ClasspCompleteOffloadWrite()
16332         // can override with success if previos WRITE USING TOKEN commands
16333         // issued for the same upper request were able to write some data.
16334         //
16335         if (transferBlockCount != 0) {
16336             status = STATUS_SUCCESS;
16337         } else {
16338 
16339             if (NT_SUCCESS(status)) {
16340                 //
16341                 // Make sure status is a failing status, without throwing away an
16342                 // already-failing status obtained from sense data.
16343                 //
16344                 status = STATUS_UNSUCCESSFUL;
16345             }
16346         }
16347 
16348         NT_ASSERT(status != STATUS_PENDING);
16349 
16350         if (!NT_SUCCESS(status)) {
16351             goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
16352         }
16353 
16354         ClasspReceiveWriteUsingTokenInformationDone(OffloadWriteContext, status);
16355         status = STATUS_PENDING;
16356         goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_Exit;
16357 
16358     } else {
16359 
16360         status = STATUS_UNSUCCESSFUL;
16361 
16362         goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
16363     }
16364 
16365     //
16366     // Error label only - not used in success case:
16367     //
16368 
16369 __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit:
16370 
16371     NT_ASSERT(!NT_SUCCESS(status));
16372 
16373     ClasspCompleteOffloadWrite(OffloadWriteContext, status);
16374 
16375 __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_Exit:
16376 
16377     //
16378     // Due to tracing a potentially freed pointer value "Irp", this trace could
16379     // be delayed beyond another offload op picking up the same pointer value
16380     // for its Irp.
16381     //
16382 
16383     TracePrint((TRACE_LEVEL_VERBOSE,
16384                 TRACE_FLAG_IOCTL,
16385                 "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Exiting function (Irp %p) with status %x.\n",
16386                 fdo,
16387                 irp,
16388                 status));
16389 
16390     return;
16391 }
16392 
16393 
16394 NTSTATUS
16395 ClasspRefreshFunctionSupportInfo(
16396     _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
16397     _In_ BOOLEAN ForceQuery
16398     )
16399 /*
16400 Routine Description:
16401 
16402     This function is to update various properties described in FDO extension's
16403     CLASS_FUNCTION_SUPPORT_INFO structure by requerying the device's VPD pages.
16404     Although this function is capable of updating any properties in the
16405     CLASS_FUNCTION_SUPPORT_INFO structure, it will initially support only
16406     a small number of proporties in the block limit data
16407 
16408 Arguments:
16409 
16410     FdoExtension : FDO extension
16411 
16412     ForceQuery : TRUE if the caller wants to force a query of the device's
16413         VPD pages.  Otherwise, the function may use cached data.
16414 
16415 Return Value:
16416 
16417     STATUS_SUCCESS or an error status
16418 
16419 --*/
16420 {
16421     NTSTATUS            status;
16422     PSCSI_REQUEST_BLOCK srb = NULL;
16423     ULONG               srbSize;
16424     CLASS_VPD_B0_DATA   blockLimitsDataNew;
16425     PCLASS_VPD_B0_DATA  blockLimitsDataOriginal;
16426     KLOCK_QUEUE_HANDLE  lockHandle;
16427     ULONG               generationCount;
16428     ULONG               changeRequestCount;
16429 
16430     //
16431     // ChangeRequestCount is incremented every time we get an unit attention with
16432     // SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED.  GenerationCount will be set to
16433     // ChangeRequestCount after CLASS_FUNCTION_SUPPORT_INFO is refreshed with the latest
16434     // VPD data.  i.e.  if both values are the same, data in
16435     // CLASS_FUNCTION_SUPPORT_INFO is current
16436     //
16437 
16438     generationCount = FdoExtension->FunctionSupportInfo->GenerationCount;
16439     changeRequestCount = FdoExtension->FunctionSupportInfo->ChangeRequestCount;
16440     if (!ForceQuery && generationCount == changeRequestCount) {
16441         return STATUS_SUCCESS;
16442     }
16443 
16444     //
16445     // Allocate an SRB for querying the device for LBP-related info if either
16446     // the Logical Block Provisioning (0xB2) or Block Limits (0xB0) VPD page
16447     // exists.
16448     //
16449     if ((FdoExtension->AdapterDescriptor != NULL) &&
16450         (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
16451         srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
16452     } else {
16453         srbSize = sizeof(SCSI_REQUEST_BLOCK);
16454     }
16455 
16456     srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, '1DcS');
16457     if (srb == NULL) {
16458         return STATUS_INSUFFICIENT_RESOURCES;
16459     }
16460 
16461     status = ClasspDeviceGetBlockLimitsVPDPage(FdoExtension,
16462                                                srb,
16463                                                srbSize,
16464                                                &blockLimitsDataNew);
16465 
16466     if (NT_SUCCESS(status)) {
16467 
16468         KeAcquireInStackQueuedSpinLock(&FdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
16469 
16470         //
16471         // If the generationCount didn't change since we looked at it last time, it means
16472         // no one has tried to update the CLASS_FUNCTION_SUPPORT_INFO data; otherwise, someone
16473         // else has beat us to it.
16474         //
16475         if (generationCount == FdoExtension->FunctionSupportInfo->GenerationCount) {
16476 
16477             blockLimitsDataOriginal = &FdoExtension->FunctionSupportInfo->BlockLimitsData;
16478             if (blockLimitsDataOriginal->CommandStatus == -1) {
16479                 //
16480                 // CommandStatus == -1 means this is the first time we have
16481                 // gotten the block limits data.
16482                 //
16483                 *blockLimitsDataOriginal = blockLimitsDataNew;
16484             } else {
16485                 //
16486                 // We only expect the Optimal Unmap Granularity (and alignment)
16487                 // to change, so those are the only parameters we update.
16488                 //
16489                 blockLimitsDataOriginal->UGAVALID = blockLimitsDataNew.UGAVALID;
16490                 blockLimitsDataOriginal->UnmapGranularityAlignment = blockLimitsDataNew.UnmapGranularityAlignment;
16491                 blockLimitsDataOriginal->OptimalUnmapGranularity = blockLimitsDataNew.OptimalUnmapGranularity;
16492             }
16493             FdoExtension->FunctionSupportInfo->GenerationCount = changeRequestCount;
16494         }
16495 
16496         KeReleaseInStackQueuedSpinLock(&lockHandle);
16497     }
16498 
16499     FREE_POOL(srb);
16500     return status;
16501 }
16502 
16503 NTSTATUS
16504 ClasspBlockLimitsDataSnapshot(
16505     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
16506     _In_ BOOLEAN ForceQuery,
16507     _Out_ PCLASS_VPD_B0_DATA BlockLimitsData,
16508     _Out_ PULONG GenerationCount
16509     )
16510 /*
16511 Routine Description:
16512 
16513     This function is to get a copy of the latest block limits data.
16514 
16515     When this function is called multiple times, GenerationCount can change (value always goes up)
16516     while BlockLimitsData stays the same.  In this case, the caller should assume BlockLimitsData
16517     has changed to different values and eventually changed back to the same state when the first
16518     call to this function was made.
16519 
16520 Arguments:
16521 
16522     FdoExtension : FDO extension
16523 
16524     ForceQuery : TRUE if the caller wants to force a query of the device's
16525         VPD pages.  Otherwise, the function may use cached data.
16526 
16527     BlockLimitsData : pointer to memory that will receive the block limits data
16528 
16529     GenerationCount : generation count of the block limit data.
16530 
16531     DataIsOutdated: set to TRUE if the BlockLimitsData is old but this function fails to
16532     query the latest data from the device due to insufficient resources
16533 
16534 Return Value:
16535 
16536     STATUS_SUCCESS or an error status
16537 
16538 --*/
16539 {
16540     NTSTATUS            status;
16541     KLOCK_QUEUE_HANDLE  lockHandle;
16542 
16543     status = ClasspRefreshFunctionSupportInfo(FdoExtension, ForceQuery);
16544 
16545     KeAcquireInStackQueuedSpinLock(&FdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
16546     *BlockLimitsData = FdoExtension->FunctionSupportInfo->BlockLimitsData;
16547     *GenerationCount = FdoExtension->FunctionSupportInfo->GenerationCount;
16548     KeReleaseInStackQueuedSpinLock(&lockHandle);
16549 
16550     return status;
16551 }
16552 
16553