xref: /reactos/drivers/storage/class/cdrom/ioctl.c (revision 3088717b)
1 /*--
2 
3 Copyright (C) Microsoft Corporation. All rights reserved.
4 
5 Module Name:
6 
7     ioctl.c
8 
9 Abstract:
10 
11     Include all funtions for processing IOCTLs
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "stddef.h"
25 #include "string.h"
26 
27 #include "ntddk.h"
28 #include "ntddstor.h"
29 #include "cdrom.h"
30 #include "ioctl.h"
31 #include "scratch.h"
32 #include "mmc.h"
33 
34 
35 #ifdef DEBUG_USE_WPP
36 #include "ioctl.tmh"
37 #endif
38 
39 
40 #define FirstDriveLetter 'C'
41 #define LastDriveLetter  'Z'
42 
43 #if DBG
44     LPCSTR READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor+1] = {
45         "Physical",
46         "Copyright",
47         "DiskKey",
48         "BCA",
49         "Manufacturer",
50         "Unknown"
51     };
52 #endif // DBG
53 
54 _IRQL_requires_max_(APC_LEVEL)
55 VOID
56 GetConfigurationDataConversionTypeAllToTypeOne(
57     _In_    FEATURE_NUMBER       RequestedFeature,
58     _In_    PSCSI_REQUEST_BLOCK  Srb,
59     _Out_   size_t *             DataLength
60     );
61 
62 _IRQL_requires_max_(APC_LEVEL)
63 VOID
64 GetConfigurationDataSynthesize(
65     _In_reads_bytes_(InputBufferSize)    PVOID           InputBuffer,
66     _In_                            ULONG           InputBufferSize,
67     _Out_writes_bytes_(OutputBufferSize)  PVOID           OutputBuffer,
68     _In_                            size_t          OutputBufferSize,
69     _In_                            FEATURE_NUMBER  StartingFeature,
70     _In_                            ULONG           RequestType,
71     _Out_                           size_t *        DataLength
72     );
73 
74 _IRQL_requires_max_(APC_LEVEL)
75 PCDB
76 RequestGetScsiPassThroughCdb(
77     _In_ PIRP Irp
78     );
79 
80 #ifdef ALLOC_PRAGMA
81 
82 #pragma alloc_text(PAGE, DeviceIsPlayActive)
83 #pragma alloc_text(PAGE, RequestHandleGetDvdRegion)
84 #pragma alloc_text(PAGE, RequestHandleReadTOC)
85 #pragma alloc_text(PAGE, RequestHandleReadTocEx)
86 #pragma alloc_text(PAGE, RequestHandleGetConfiguration)
87 #pragma alloc_text(PAGE, RequestHandleGetDriveGeometry)
88 #pragma alloc_text(PAGE, RequestHandleDiskVerify)
89 #pragma alloc_text(PAGE, RequestHandleCheckVerify)
90 #pragma alloc_text(PAGE, RequestHandleFakePartitionInfo)
91 #pragma alloc_text(PAGE, RequestHandleEjectionControl)
92 #pragma alloc_text(PAGE, RequestHandleEnableStreaming)
93 #pragma alloc_text(PAGE, RequestHandleSendOpcInformation)
94 #pragma alloc_text(PAGE, RequestHandleGetPerformance)
95 #pragma alloc_text(PAGE, RequestHandleMcnSyncFakeIoctl)
96 #pragma alloc_text(PAGE, RequestHandleLoadEjectMedia)
97 #pragma alloc_text(PAGE, RequestHandleReserveRelease)
98 #pragma alloc_text(PAGE, RequestHandlePersistentReserve)
99 #pragma alloc_text(PAGE, DeviceHandleRawRead)
100 #pragma alloc_text(PAGE, DeviceHandlePlayAudioMsf)
101 #pragma alloc_text(PAGE, DeviceHandleReadQChannel)
102 #pragma alloc_text(PAGE, ReadQChannel)
103 #pragma alloc_text(PAGE, DeviceHandlePauseAudio)
104 #pragma alloc_text(PAGE, DeviceHandleResumeAudio)
105 #pragma alloc_text(PAGE, DeviceHandleSeekAudioMsf)
106 #pragma alloc_text(PAGE, DeviceHandleStopAudio)
107 #pragma alloc_text(PAGE, DeviceHandleGetSetVolume)
108 #pragma alloc_text(PAGE, DeviceHandleReadDvdStructure)
109 #pragma alloc_text(PAGE, ReadDvdStructure)
110 #pragma alloc_text(PAGE, DeviceHandleDvdEndSession)
111 #pragma alloc_text(PAGE, DeviceHandleDvdStartSessionReadKey)
112 #pragma alloc_text(PAGE, DvdStartSessionReadKey)
113 #pragma alloc_text(PAGE, DeviceHandleDvdSendKey)
114 #pragma alloc_text(PAGE, DvdSendKey)
115 #pragma alloc_text(PAGE, DeviceHandleSetReadAhead)
116 #pragma alloc_text(PAGE, DeviceHandleSetSpeed)
117 #pragma alloc_text(PAGE, RequestHandleExclusiveAccessQueryLockState)
118 #pragma alloc_text(PAGE, RequestHandleExclusiveAccessLockDevice)
119 #pragma alloc_text(PAGE, RequestHandleExclusiveAccessUnlockDevice)
120 #pragma alloc_text(PAGE, RequestHandleScsiPassThrough)
121 #pragma alloc_text(PAGE, RequestGetScsiPassThroughCdb)
122 #pragma alloc_text(PAGE, GetConfigurationDataConversionTypeAllToTypeOne)
123 #pragma alloc_text(PAGE, GetConfigurationDataSynthesize)
124 
125 #endif
126 
127 NTSTATUS
RequestHandleUnknownIoctl(_In_ WDFDEVICE Device,_In_ WDFREQUEST Request)128 RequestHandleUnknownIoctl(
129     _In_ WDFDEVICE    Device,
130     _In_ WDFREQUEST   Request
131     )
132 /*++
133 
134 Routine Description:
135 
136     All unknown IOCTLs will be forward to lower level driver.
137 
138 Arguments:
139 
140     Device - device object
141     Request - request to be handled
142 
143 Return Value:
144 
145     NTSTATUS
146 
147 --*/
148 {
149     NTSTATUS                    status = STATUS_UNSUCCESSFUL;
150     PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
151     PCDROM_REQUEST_CONTEXT      requestContext = RequestGetContext(Request);
152     BOOLEAN                     syncRequired = requestContext->SyncRequired;
153 
154     ULONG                       sendOptionsFlags = 0;
155     BOOLEAN                     requestSent = FALSE;
156 
157     WdfRequestFormatRequestUsingCurrentType(Request);
158 
159     if (syncRequired)
160     {
161         sendOptionsFlags = WDF_REQUEST_SEND_OPTION_SYNCHRONOUS;
162     }
163     else
164     {
165         WdfRequestSetCompletionRoutine(Request, RequestDummyCompletionRoutine, NULL);
166     }
167 
168     status = RequestSend(deviceExtension,
169                          Request,
170                          deviceExtension->IoTarget,
171                          sendOptionsFlags,
172                          &requestSent);
173 
174     if (requestSent)
175     {
176         if (syncRequired)
177         {
178             // the request needs to be completed here.
179             RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
180         }
181     }
182     else
183     {
184         // failed to send the request to IoTarget
185         RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
186     }
187 
188     return status;
189 }
190 
_IRQL_requires_max_(APC_LEVEL)191 _IRQL_requires_max_(APC_LEVEL)
192 BOOLEAN
193 DeviceIsPlayActive(
194     _In_ WDFDEVICE Device
195     )
196 /*++
197 
198 Routine Description:
199 
200     This routine determines if the cd is currently playing music.
201 
202 Arguments:
203 
204     Device - Device object.
205 
206 Return Value:
207 
208     BOOLEAN - TRUE if the device is playing music.
209 
210 --*/
211 {
212     NTSTATUS                status = STATUS_SUCCESS;
213     PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
214     PSUB_Q_CURRENT_POSITION currentBuffer;
215     size_t                  bytesRead = 0;
216 
217     PAGED_CODE ();
218 
219     // if we don't think it is playing audio, don't bother checking.
220     if (!deviceExtension->DeviceAdditionalData.PlayActive)
221     {
222         return FALSE;
223     }
224 
225     // Allocate the required memory
226     NT_ASSERT(sizeof(SUB_Q_CURRENT_POSITION) >= sizeof(CDROM_SUB_Q_DATA_FORMAT));
227     currentBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
228                                           sizeof(SUB_Q_CURRENT_POSITION),
229                                           CDROM_TAG_PLAY_ACTIVE);
230     if (currentBuffer == NULL)
231     {
232         return FALSE;
233     }
234 
235     // set the options in the output buffer format
236     ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION;
237     ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0;
238 
239     // Send SCSI command to read Q Channel information.
240     status = ReadQChannel(deviceExtension,
241                           NULL,
242                           currentBuffer,
243                           sizeof(CDROM_SUB_Q_DATA_FORMAT),
244                           currentBuffer,
245                           sizeof(SUB_Q_CURRENT_POSITION),
246                           &bytesRead);
247 
248     if (!NT_SUCCESS(status))
249     {
250         ExFreePool(currentBuffer);
251         return FALSE;
252     }
253 
254     // update the playactive flag appropriately
255     if (currentBuffer->Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS)
256     {
257         deviceExtension->DeviceAdditionalData.PlayActive = TRUE;
258     }
259     else
260     {
261         deviceExtension->DeviceAdditionalData.PlayActive = FALSE;
262     }
263 
264     ExFreePool(currentBuffer);
265 
266     return deviceExtension->DeviceAdditionalData.PlayActive;
267 }
268 
269 NTSTATUS
RequestHandleGetInquiryData(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)270 RequestHandleGetInquiryData(
271     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
272     _In_  WDFREQUEST               Request,
273     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
274     _Out_ size_t *                 DataLength)
275 /*++
276 
277 Routine Description:
278 
279    Handler for IOCTL_CDROM_GET_INQUIRY_DATA
280 
281 Arguments:
282 
283     DeviceExtension - device context
284     Request - request to be handled
285     RequestParameters - request parameter
286     DataLength - transferred data length
287 
288 Return Value:
289 
290     NTSTATUS
291 
292 --*/
293 {
294     NTSTATUS    status = STATUS_SUCCESS;
295     PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData);
296 
297     *DataLength = 0;
298 
299     if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
300     {
301         status = STATUS_BUFFER_TOO_SMALL;
302     }
303     else
304     {
305         PVOID outputBuffer = NULL;
306 
307         *DataLength = min(cdData->CachedInquiryDataByteCount,
308                           RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
309 
310         status = WdfRequestRetrieveOutputBuffer(Request,
311                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
312                                                 &outputBuffer,
313                                                 NULL);
314 
315         if (NT_SUCCESS(status) &&
316             (outputBuffer != NULL))
317         {
318             // Always copy as much data as possible
319             RtlCopyMemory(outputBuffer,
320                           cdData->CachedInquiryData,
321                           *DataLength);
322         }
323 
324         // and finally decide between two possible status values
325         if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < cdData->CachedInquiryDataByteCount)
326         {
327             status = STATUS_BUFFER_OVERFLOW;
328         }
329     }
330 
331     return status;
332 }
333 
334 
335 NTSTATUS
RequestHandleGetMediaTypeEx(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_Out_ size_t * DataLength)336 RequestHandleGetMediaTypeEx(
337     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
338     _In_  WDFREQUEST               Request,
339     _Out_ size_t *                 DataLength
340     )
341 /*++
342 
343 Routine Description:
344 
345    Handler for IOCTL_STORAGE_GET_MEDIA_TYPES_EX
346 
347 Arguments:
348 
349     DeviceExtension - device context
350     Request - request to be handled
351     DataLength - transferred data length
352 
353 Return Value:
354 
355     NTSTATUS
356 
357 --*/
358 {
359     NTSTATUS            status = STATUS_SUCCESS;
360     PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
361 
362     PGET_MEDIA_TYPES        mediaTypes = NULL;
363     PDEVICE_MEDIA_INFO      mediaInfo = NULL; //&mediaTypes->MediaInfo[0];
364     ULONG                   sizeNeeded = 0;
365     PZERO_POWER_ODD_INFO    zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
366 
367     *DataLength = 0;
368 
369     // Must run below dispatch level.
370     if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
371     {
372         NT_ASSERT(FALSE);
373         return STATUS_INVALID_LEVEL;
374     }
375 
376     sizeNeeded = sizeof(GET_MEDIA_TYPES);
377 
378     // IsMmc is static...
379     if (cdData->Mmc.IsMmc)
380     {
381         sizeNeeded += sizeof(DEVICE_MEDIA_INFO) * 1; // return two media types
382     }
383 
384     status = WdfRequestRetrieveOutputBuffer(Request,
385                                             sizeNeeded,
386                                             (PVOID*)&mediaTypes,
387                                             NULL);
388 
389     if (NT_SUCCESS(status) &&
390         (mediaTypes != NULL))
391     {
392         mediaInfo = &mediaTypes->MediaInfo[0];
393 
394         RtlZeroMemory(mediaTypes, sizeNeeded);
395 
396         // ISSUE-2000/5/11-henrygab - need to update GET_MEDIA_TYPES_EX
397 
398         mediaTypes->DeviceType = cdData->DriveDeviceType;
399 
400         mediaTypes->MediaInfoCount = 1;
401         mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = CD_ROM;
402         mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
403         mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_ONLY;
404         mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = DeviceExtension->DiskGeometry.Cylinders.QuadPart;
405         mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = DeviceExtension->DiskGeometry.TracksPerCylinder;
406         mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = DeviceExtension->DiskGeometry.SectorsPerTrack;
407         mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = DeviceExtension->DiskGeometry.BytesPerSector;
408 
409         if (cdData->Mmc.IsMmc)
410         {
411             // also report a removable disk
412             mediaTypes->MediaInfoCount += 1;
413 
414             mediaInfo++;
415             mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia;
416             mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
417             mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
418             mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = DeviceExtension->DiskGeometry.Cylinders.QuadPart;
419             mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = DeviceExtension->DiskGeometry.TracksPerCylinder;
420             mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = DeviceExtension->DiskGeometry.SectorsPerTrack;
421             mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = DeviceExtension->DiskGeometry.BytesPerSector;
422             mediaInfo--;
423 
424         }
425 
426         // Status will either be success, if media is present, or no media.
427         // It would be optimal to base from density code and medium type, but not all devices
428         // have values for these fields.
429 
430         // Send a TUR to determine if media is present, only if the device is not in ZPODD mode.
431         if ((!EXCLUSIVE_MODE(cdData) ||
432              EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request))) &&
433             ((zpoddInfo == NULL) ||
434              (zpoddInfo->InZeroPowerState == FALSE)))
435         {
436             SCSI_REQUEST_BLOCK  srb;
437             PCDB                cdb = (PCDB)srb.Cdb;
438 
439             RtlZeroMemory(&srb,sizeof(SCSI_REQUEST_BLOCK));
440 
441             srb.CdbLength = 6;
442             cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
443 
444             srb.TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT;
445 
446             status = DeviceSendSrbSynchronously(DeviceExtension->Device,
447                                                 &srb,
448                                                 NULL,
449                                                 0,
450                                                 FALSE,
451                                                 Request);
452 
453             if (NT_SUCCESS(status))
454             {
455                 // set the disk's media as current if we can write to it.
456                 if (cdData->Mmc.IsMmc && cdData->Mmc.WriteAllowed)
457                 {
458                     mediaInfo++;
459                     SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
460                              MEDIA_CURRENTLY_MOUNTED);
461                     mediaInfo--;
462                 }
463                 else
464                 {
465                     SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
466                              MEDIA_CURRENTLY_MOUNTED);
467                 }
468             }
469             else
470             {
471                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
472                            "RequestHandleGetMediaTypeEx: GET_MEDIA_TYPES status of TUR - %lx\n", status));
473             }
474         }
475 
476         // per legacy cdrom behavior, always return success
477         status = STATUS_SUCCESS;
478     }
479 
480     *DataLength = sizeNeeded;
481 
482     return status;
483 }
484 
_IRQL_requires_max_(APC_LEVEL)485 _IRQL_requires_max_(APC_LEVEL)
486 NTSTATUS
487 RequestHandleGetDvdRegion(
488     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
489     _In_  WDFREQUEST               Request,
490     _Out_ size_t *                 DataLength
491     )
492 /*++
493 
494 Routine Description:
495 
496    Handler for IOCTL_DVD_GET_REGION
497 
498 Arguments:
499 
500     DeviceExtension - device context
501     Request - request to be handled
502     DataLength - transferred data length
503 
504 Return Value:
505 
506     NTSTATUS
507 
508 --*/
509 {
510     NTSTATUS            status = STATUS_SUCCESS;
511 
512     PVOID               outputBuffer = NULL;
513     size_t              bytesReturned = 0;
514 
515     PDVD_COPY_PROTECT_KEY       copyProtectKey = NULL;
516     ULONG                       keyLength = 0;
517     PDVD_DESCRIPTOR_HEADER      dvdHeader;
518     PDVD_COPYRIGHT_DESCRIPTOR   copyRightDescriptor;
519     PDVD_REGION                 dvdRegion = NULL;
520     PDVD_READ_STRUCTURE         readStructure = NULL;
521     PDVD_RPC_KEY                rpcKey;
522 
523     PAGED_CODE ();
524 
525     TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
526                 "RequestHandleGetDvdRegion: [%p] IOCTL_DVD_GET_REGION\n", Request));
527 
528     *DataLength = 0;
529 
530     // reject the request if it's not a DVD device.
531     if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
532     {
533         status = STATUS_INVALID_DEVICE_REQUEST;
534     }
535 
536     if (NT_SUCCESS(status))
537     {
538         status = WdfRequestRetrieveOutputBuffer(Request,
539                                                 sizeof(DVD_REGION),
540                                                 &outputBuffer,
541                                                 NULL);
542     }
543 
544     if (NT_SUCCESS(status))
545     {
546         // figure out how much data buffer we need
547         keyLength = max((sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR)),
548                         sizeof(DVD_READ_STRUCTURE));
549         keyLength = max(keyLength,
550                         DVD_RPC_KEY_LENGTH);
551 
552         // round the size to nearest ULONGLONG -- why?
553         // could this be to deal with device alignment issues?
554         keyLength += sizeof(ULONGLONG) - (keyLength & (sizeof(ULONGLONG) - 1));
555 
556         readStructure = ExAllocatePoolWithTag(NonPagedPoolNx,
557                                               keyLength,
558                                               DVD_TAG_READ_KEY);
559         if (readStructure == NULL)
560         {
561             status = STATUS_INSUFFICIENT_RESOURCES;
562         }
563     }
564 
565     if (NT_SUCCESS(status))
566     {
567         RtlZeroMemory (readStructure, keyLength);
568         readStructure->Format = DvdCopyrightDescriptor;
569 
570         // use READ_STRUCTURE to read copyright descriptor
571         status = ReadDvdStructure(DeviceExtension,
572                                   Request,
573                                   readStructure,
574                                   keyLength,
575                                   readStructure,
576                                   sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR),
577                                   &bytesReturned);
578     }
579 
580     if (NT_SUCCESS(status))
581     {
582         // we got the copyright descriptor, so now get the region if possible
583         dvdHeader = (PDVD_DESCRIPTOR_HEADER) readStructure;
584         copyRightDescriptor = (PDVD_COPYRIGHT_DESCRIPTOR) dvdHeader->Data;
585 
586         // the original irp's systembuffer has a copy of the info that
587         // should be passed down in the request
588         dvdRegion = outputBuffer;
589 
590         dvdRegion->CopySystem = copyRightDescriptor->CopyrightProtectionType;
591         dvdRegion->RegionData = copyRightDescriptor->RegionManagementInformation;
592 
593         // now reuse the buffer to request the copy protection info
594         copyProtectKey = (PDVD_COPY_PROTECT_KEY) readStructure;
595         RtlZeroMemory (copyProtectKey, DVD_RPC_KEY_LENGTH);
596         copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
597         copyProtectKey->KeyType = DvdGetRpcKey;
598 
599         // send a request for READ_KEY
600         status = DvdStartSessionReadKey(DeviceExtension,
601                                         IOCTL_DVD_READ_KEY,
602                                         Request,
603                                         copyProtectKey,
604                                         DVD_RPC_KEY_LENGTH,
605                                         copyProtectKey,
606                                         DVD_RPC_KEY_LENGTH,
607                                         &bytesReturned);
608     }
609 
610     if (NT_SUCCESS(status))
611     {
612         // the request succeeded.  if a supported scheme is returned,
613         // then return the information to the caller
614         rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData;
615 
616         if (rpcKey->RpcScheme == 1)
617         {
618             if (rpcKey->TypeCode)
619             {
620                 dvdRegion->SystemRegion = ~rpcKey->RegionMask;
621                 dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
622             }
623             else
624             {
625                 // the drive has not been set for any region
626                 dvdRegion->SystemRegion = 0;
627                 dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
628             }
629 
630             *DataLength = sizeof(DVD_REGION);
631         }
632         else
633         {
634             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
635                         "RequestHandleGetDvdRegion => rpcKey->RpcScheme != 1\n"));
636             status = STATUS_INVALID_DEVICE_REQUEST;
637         }
638     }
639 
640     // Clean up
641     if (readStructure != NULL)
642     {
643         ExFreePool(readStructure);
644     }
645 
646     return status;
647 }
648 
649 NTSTATUS
RequestValidateRawRead(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)650 RequestValidateRawRead(
651     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
652     _In_  WDFREQUEST               Request,
653     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
654     _Out_ size_t *                 DataLength
655     )
656 /*++
657 
658 Routine Description:
659 
660    Validate request of IOCTL_CDROM_RAW_READ
661 
662 Arguments:
663 
664     DeviceExtension - device context
665     Request - request to be handled
666     RequestParameters - request parameter
667     DataLength - transferred data length
668 
669 Return Value:
670 
671     NTSTATUS
672 
673 --*/
674 {
675     NTSTATUS            status = STATUS_SUCCESS;
676     PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
677 
678     PVOID               inputBuffer = NULL;
679     PIRP                irp = NULL;
680     PIO_STACK_LOCATION  currentStack = NULL;
681 
682     LARGE_INTEGER       startingOffset = {0};
683     ULONGLONG           transferBytes = 0;
684     ULONGLONG           endOffset;
685     ULONGLONG           mdlBytes;
686     RAW_READ_INFO       rawReadInfo = {0};
687 
688     *DataLength = 0;
689 
690     irp = WdfRequestWdmGetIrp(Request);
691     currentStack = IoGetCurrentIrpStackLocation(irp);
692 
693     status = WdfRequestRetrieveInputBuffer(Request,
694                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
695                                            &inputBuffer,
696                                            NULL);
697 
698     // Check that ending sector is on disc and buffers are there and of
699     // correct size.
700     if (NT_SUCCESS(status) &&
701         (RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer == NULL))
702     {
703         //  This is a call from user space.  This is the only time that we need to validate parameters.
704         //  Validate the input and get the input buffer into Type3InputBuffer
705         //  so the rest of the code will be uniform.
706         if (inputBuffer != NULL)
707         {
708             currentStack->Parameters.DeviceIoControl.Type3InputBuffer = inputBuffer;
709             RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer = inputBuffer;
710 
711             if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(RAW_READ_INFO))
712             {
713                 *DataLength = sizeof(RAW_READ_INFO);
714                 status = STATUS_BUFFER_TOO_SMALL;
715             }
716         }
717         else
718         {
719             status = STATUS_INVALID_PARAMETER;
720         }
721     }
722 
723     if (NT_SUCCESS(status))
724     {
725         //  Since this ioctl is METHOD_OUT_DIRECT, we need to copy away the input buffer before interpreting it.
726         //  This prevents a malicious app from messing with the input buffer while we are interpreting it.
727         rawReadInfo = *(PRAW_READ_INFO)RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer;
728 
729         startingOffset.QuadPart = rawReadInfo.DiskOffset.QuadPart;
730 
731         if ((rawReadInfo.TrackMode == CDDA)        ||
732             (rawReadInfo.TrackMode == YellowMode2) ||
733             (rawReadInfo.TrackMode == XAForm2)     )
734         {
735             transferBytes = (ULONGLONG)rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
736         }
737         else if (rawReadInfo.TrackMode == RawWithSubCode)
738         {
739             transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_SUBCODE_SIZE;
740         }
741         else if (rawReadInfo.TrackMode == RawWithC2)
742         {
743             transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_SIZE;
744         }
745         else if (rawReadInfo.TrackMode == RawWithC2AndSubCode)
746         {
747             transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE;
748         }
749         else
750         {
751             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
752                       "RequestValidateRawRead: Invalid TrackMode type %x for XA read\n",
753                       rawReadInfo.TrackMode
754                       ));
755         }
756 
757         endOffset = (ULONGLONG)rawReadInfo.SectorCount * COOKED_SECTOR_SIZE;
758         endOffset += startingOffset.QuadPart;
759 
760         // check for overflows....
761         if (rawReadInfo.SectorCount == 0)
762         {
763             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
764                         "RequestValidateRawRead: Invalid I/O parameters for XA "
765                         "Read (zero sectors requested)\n"));
766             status = STATUS_INVALID_PARAMETER;
767         }
768         else if (transferBytes < (ULONGLONG)(rawReadInfo.SectorCount))
769         {
770             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
771                         "RequestValidateRawRead: Invalid I/O parameters for XA "
772                         "Read (TransferBytes Overflow)\n"));
773             status = STATUS_INVALID_PARAMETER;
774         }
775         else if (endOffset < (ULONGLONG)startingOffset.QuadPart)
776         {
777             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
778                         "RequestValidateRawRead: Invalid I/O parameters for XA "
779                         "Read (EndingOffset Overflow)\n"));
780             status = STATUS_INVALID_PARAMETER;
781         }
782         else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < transferBytes)
783         {
784             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
785                         "RequestValidateRawRead: Invalid I/O parameters for XA "
786                         "Read (Bad buffer size)\n"));
787             status = STATUS_INVALID_PARAMETER;
788         }
789         else if (endOffset > (ULONGLONG)DeviceExtension->PartitionLength.QuadPart)
790         {
791             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
792                         "RequestValidateRawRead: Invalid I/O parameters for XA "
793                         "Read (Request Out of Bounds)\n"));
794             status = STATUS_INVALID_PARAMETER;
795         }
796     }
797 
798     if (NT_SUCCESS(status))
799     {
800         // cannot validate the MdlAddress, since it is not included in any
801         // other location per the DDK and file system calls.
802 
803         // validate the mdl describes at least the number of bytes
804         // requested from us.
805         mdlBytes = (ULONGLONG)MmGetMdlByteCount(irp->MdlAddress);
806         if (mdlBytes < transferBytes)
807         {
808             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
809                         "RequestValidateRawRead: Invalid MDL %s, Irp %p\n",
810                         "size (5)", irp));
811             status = STATUS_INVALID_PARAMETER;
812         }
813     }
814 
815     if (NT_SUCCESS(status))
816     {
817         // check the buffer for alignment
818         // This is important for x86 as some busses (ie ATAPI)
819         // require word-aligned buffers.
820         if ( ((ULONG_PTR)MmGetMdlVirtualAddress(irp->MdlAddress)) &
821              DeviceExtension->AdapterDescriptor->AlignmentMask )
822         {
823             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
824                       "RequestValidateRawRead: Invalid I/O parameters for "
825                       "XA Read (Buffer %p not aligned with mask %x\n",
826                       RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer,
827                       DeviceExtension->AdapterDescriptor->AlignmentMask));
828             status = STATUS_INVALID_PARAMETER;
829         }
830     }
831 
832     if (NT_SUCCESS(status))
833     {
834         // Validate the request is not too large for the adapter
835         BOOLEAN bufferIsPageAligned = FALSE;
836         ULONG   maxLength = 0;
837 
838         // if buffer is not page-aligned, then subtract one as the
839         // transfer could cross a page boundary.
840         if ((((ULONG_PTR)MmGetMdlVirtualAddress(irp->MdlAddress)) & (PAGE_SIZE-1)) == 0)
841         {
842             bufferIsPageAligned = TRUE;
843         }
844 
845         if (bufferIsPageAligned)
846         {
847             maxLength = cdData->MaxPageAlignedTransferBytes;
848         }
849         else
850         {
851             maxLength = cdData->MaxUnalignedTransferBytes;
852         }
853 
854         if (transferBytes > maxLength)
855         {
856             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
857                       "RequestValidateRawRead: The XA Read (type %x) would require %I64x bytes, "
858                       "but the adapter can only handle %x bytes (for a%saligned buffer)\n",
859                       rawReadInfo.TrackMode,
860                       transferBytes,
861                       maxLength,
862                       (bufferIsPageAligned ? " " : "n un")
863                       ));
864             status = STATUS_INVALID_PARAMETER;
865         }
866     }
867 
868     if (NT_SUCCESS(status))
869     {
870         //
871         // HACKHACK - REF #0001
872         // The retry count will be in this irp's IRP_MN function,
873         // as the new irp was freed, and we therefore cannot use
874         // this irp's next stack location for this function.
875         // This may be a good location to store this info for
876         // when we remove RAW_READ (mode switching), as we will
877         // no longer have the nextIrpStackLocation to play with
878         // when that occurs
879         //
880         currentStack->MinorFunction = MAXIMUM_RETRIES; // HACKHACK - REF #0001
881     }
882 
883     return status;
884 }
885 
886 NTSTATUS
RequestValidateReadTocEx(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)887 RequestValidateReadTocEx(
888     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
889     _In_  WDFREQUEST               Request,
890     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
891     _Out_ size_t *                 DataLength
892     )
893 /*++
894 
895 Routine Description:
896 
897    Validate request of IOCTL_CDROM_READ_TOC_EX
898 
899 Arguments:
900 
901     DeviceExtension - device context
902     Request - request to be handled
903     RequestParameters - request parameter
904     DataLength - transferred data length
905 
906 Return Value:
907 
908     NTSTATUS
909 
910 --*/
911 {
912     NTSTATUS            status = STATUS_SUCCESS;
913 
914     PCDROM_READ_TOC_EX  inputBuffer = NULL;
915 
916     *DataLength = 0;
917 
918     if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
919         sizeof(CDROM_READ_TOC_EX))
920     {
921         status = STATUS_INFO_LENGTH_MISMATCH;
922     }
923     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
924              MINIMUM_CDROM_READ_TOC_EX_SIZE)
925     {
926         status = STATUS_BUFFER_TOO_SMALL;
927         *DataLength = MINIMUM_CDROM_READ_TOC_EX_SIZE;
928     }
929     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
930              ((USHORT)-1))
931     {
932         status = STATUS_INVALID_PARAMETER;
933     }
934     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
935              DeviceExtension->AdapterDescriptor->AlignmentMask)
936     {
937         status = STATUS_INVALID_PARAMETER;
938     }
939 
940     if (NT_SUCCESS(status))
941     {
942         status = WdfRequestRetrieveInputBuffer(Request,
943                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
944                                                &inputBuffer,
945                                                NULL);
946     }
947 
948     if (NT_SUCCESS(status))
949     {
950         if ((inputBuffer->Reserved1 != 0) ||
951             (inputBuffer->Reserved2 != 0) ||
952             (inputBuffer->Reserved3 != 0))
953         {
954             status = STATUS_INVALID_PARAMETER;
955         }
956         // NOTE: when adding new formats, ensure that first two bytes
957         //       specify the amount of additional data available.
958         else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_TOC     ) ||
959                  (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_FULL_TOC) ||
960                  (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_CDTEXT  ))
961         {
962             // SessionTrack field is used
963         }
964         else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) ||
965                  (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_PMA)     ||
966                  (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_ATIP))
967         {
968             // SessionTrack field is reserved
969             if (inputBuffer->SessionTrack != 0)
970             {
971                 status = STATUS_INVALID_PARAMETER;
972             }
973         }
974         else
975         {
976             status = STATUS_INVALID_PARAMETER;
977         }
978     }
979 
980     return status;
981 }
982 
983 NTSTATUS
RequestValidateReadToc(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)984 RequestValidateReadToc(
985     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
986     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
987     _Out_ size_t *                 DataLength
988     )
989 /*++
990 
991 Routine Description:
992 
993    Validate request of IOCTL_CDROM_READ_TOC
994 
995 Arguments:
996 
997     DeviceExtension - device context
998     RequestParameters - request parameter
999     DataLength - transferred data length
1000 
1001 Return Value:
1002 
1003     NTSTATUS
1004 
1005 --*/
1006 {
1007     NTSTATUS            status = STATUS_SUCCESS;
1008 
1009     *DataLength = 0;
1010 
1011     if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
1012         sizeof(CDROM_TOC))
1013     {
1014         // they didn't request the entire TOC -- use _EX version
1015         // for partial transfers and such.
1016         status = STATUS_BUFFER_TOO_SMALL;
1017         *DataLength = sizeof(CDROM_TOC);
1018     }
1019     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
1020              DeviceExtension->AdapterDescriptor->AlignmentMask)
1021     {
1022         status = STATUS_INVALID_PARAMETER;
1023     }
1024 
1025     return status;
1026 }
1027 
1028 NTSTATUS
RequestValidateGetLastSession(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1029 RequestValidateGetLastSession(
1030     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1031     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1032     _Out_ size_t *                 DataLength
1033     )
1034 /*++
1035 
1036 Routine Description:
1037 
1038    Validate request of IOCTL_CDROM_GET_LAST_SESSION
1039 
1040 Arguments:
1041 
1042     DeviceExtension - device context
1043     RequestParameters - request parameter
1044     DataLength - transferred data length
1045 
1046 Return Value:
1047 
1048     NTSTATUS
1049 
1050 --*/
1051 {
1052     NTSTATUS            status = STATUS_SUCCESS;
1053 
1054     *DataLength = 0;
1055 
1056     if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
1057         sizeof(CDROM_TOC_SESSION_DATA))
1058     {
1059         status = STATUS_BUFFER_TOO_SMALL;
1060         *DataLength = sizeof(CDROM_TOC_SESSION_DATA);
1061     }
1062     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
1063              DeviceExtension->AdapterDescriptor->AlignmentMask)
1064     {
1065         status = STATUS_INVALID_PARAMETER;
1066     }
1067 
1068     return status;
1069 }
1070 
1071 NTSTATUS
RequestValidateReadQChannel(_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1072 RequestValidateReadQChannel(
1073     _In_  WDFREQUEST               Request,
1074     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1075     _Out_ size_t *                 DataLength
1076     )
1077 /*++
1078 
1079 Routine Description:
1080 
1081    Validate request of IOCTL_CDROM_READ_Q_CHANNEL
1082 
1083 Arguments:
1084 
1085     Request - request to be handled
1086     RequestParameters - request parameter
1087     DataLength - transferred data length
1088 
1089 Return Value:
1090 
1091     NTSTATUS
1092 
1093 --*/
1094 {
1095     NTSTATUS                 status = STATUS_SUCCESS;
1096     PCDROM_SUB_Q_DATA_FORMAT inputBuffer = NULL;
1097     ULONG                    transferByteCount = 0;
1098 
1099     *DataLength = 0;
1100 
1101     if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
1102        sizeof(CDROM_SUB_Q_DATA_FORMAT))
1103     {
1104         status = STATUS_INFO_LENGTH_MISMATCH;
1105     }
1106 
1107     if (NT_SUCCESS(status))
1108     {
1109         status = WdfRequestRetrieveInputBuffer(Request,
1110                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1111                                                &inputBuffer,
1112                                                NULL);
1113     }
1114 
1115     if (NT_SUCCESS(status))
1116     {
1117         // check for all valid types of request
1118         if (inputBuffer->Format == IOCTL_CDROM_CURRENT_POSITION)
1119         {
1120             transferByteCount = sizeof(SUB_Q_CURRENT_POSITION);
1121         }
1122         else if (inputBuffer->Format == IOCTL_CDROM_MEDIA_CATALOG)
1123         {
1124             transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
1125         }
1126         else if (inputBuffer->Format == IOCTL_CDROM_TRACK_ISRC)
1127         {
1128             transferByteCount = sizeof(SUB_Q_TRACK_ISRC);
1129         }
1130         else
1131         {
1132             // Format not valid
1133             status = STATUS_INVALID_PARAMETER;
1134         }
1135     }
1136 
1137     if (NT_SUCCESS(status))
1138     {
1139         if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
1140             transferByteCount)
1141         {
1142             status = STATUS_BUFFER_TOO_SMALL;
1143             *DataLength = transferByteCount;
1144         }
1145     }
1146 
1147     return status;
1148 }
1149 
1150 NTSTATUS
RequestValidateDvdReadStructure(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1151 RequestValidateDvdReadStructure(
1152     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1153     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1154     _Out_ size_t *                 DataLength
1155     )
1156 /*++
1157 
1158 Routine Description:
1159 
1160    Validate request of IOCTL_DVD_READ_STRUCTURE
1161 
1162 Arguments:
1163 
1164     DeviceExtension - device context
1165     RequestParameters - request parameter
1166     DataLength - transferred data length
1167 
1168 Return Value:
1169 
1170     NTSTATUS
1171 
1172 --*/
1173 {
1174     NTSTATUS    status = STATUS_SUCCESS;
1175 
1176     *DataLength = 0;
1177 
1178     if (NT_SUCCESS(status))
1179     {
1180         if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
1181            sizeof(DVD_READ_STRUCTURE))
1182         {
1183             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
1184                         "RequestValidateDvdReadStructure - input buffer "
1185                         "length too small (was %d should be %d)\n",
1186                         (int)RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1187                         sizeof(DVD_READ_STRUCTURE)));
1188             status = STATUS_INVALID_PARAMETER;
1189         }
1190         else if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
1191                 sizeof(READ_DVD_STRUCTURES_HEADER))
1192         {
1193 
1194             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
1195                         "RequestValidateDvdReadStructure - output buffer "
1196                         "cannot hold header information\n"));
1197             status = STATUS_BUFFER_TOO_SMALL;
1198             *DataLength = sizeof(READ_DVD_STRUCTURES_HEADER);
1199         }
1200         else if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
1201                 MAXUSHORT)
1202         {
1203             // key length must fit in two bytes
1204             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
1205                         "RequestValidateDvdReadStructure - output buffer "
1206                         "too large\n"));
1207             status = STATUS_INVALID_PARAMETER;
1208         }
1209         else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
1210                  DeviceExtension->AdapterDescriptor->AlignmentMask)
1211         {
1212             status = STATUS_INVALID_PARAMETER;
1213         }
1214         else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
1215         {
1216             // reject the request if it's not a DVD device.
1217             status = STATUS_INVALID_DEVICE_REQUEST;
1218         }
1219     }
1220 
1221     return status;
1222 }
1223 
1224 NTSTATUS
RequestValidateDvdStartSession(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1225 RequestValidateDvdStartSession(
1226     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1227     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1228     _Out_ size_t *                 DataLength
1229     )
1230 /*++
1231 
1232 Routine Description:
1233 
1234     Validate request of IOCTL_DVD_START_SESSION
1235 
1236 Arguments:
1237 
1238     DeviceExtension - device context
1239     RequestParameters - request parameter
1240     DataLength - transferred data length
1241 
1242 Return Value:
1243 
1244     NTSTATUS
1245 
1246 --*/
1247 {
1248     NTSTATUS    status = STATUS_SUCCESS;
1249 
1250     *DataLength = 0;
1251 
1252     if (NT_SUCCESS(status))
1253     {
1254         if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
1255            sizeof(DVD_SESSION_ID))
1256         {
1257             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
1258                         "RequestValidateDvdStartSession: DVD_START_SESSION - output "
1259                         "buffer too small\n"));
1260             status = STATUS_BUFFER_TOO_SMALL;
1261             *DataLength = sizeof(DVD_SESSION_ID);
1262         }
1263         else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
1264         {
1265             // reject the request if it's not a DVD device.
1266             status = STATUS_INVALID_DEVICE_REQUEST;
1267         }
1268     }
1269 
1270     return status;
1271 }
1272 
1273 NTSTATUS
RequestValidateDvdSendKey(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1274 RequestValidateDvdSendKey(
1275     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1276     _In_  WDFREQUEST               Request,
1277     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1278     _Out_ size_t *                 DataLength
1279     )
1280 /*++
1281 
1282 Routine Description:
1283 
1284    Validate request of IOCTL_DVD_SEND_KEY, IOCTL_DVD_SEND_KEY2
1285 
1286 Arguments:
1287 
1288     DeviceExtension - device context
1289     Request - request to be handled
1290     RequestParameters - request parameter
1291     DataLength - transferred data length
1292 
1293 Return Value:
1294 
1295     NTSTATUS
1296 
1297 --*/
1298 {
1299     NTSTATUS                status = STATUS_SUCCESS;
1300     PDVD_COPY_PROTECT_KEY   key = NULL;
1301 
1302     *DataLength = 0;
1303 
1304     status = WdfRequestRetrieveInputBuffer(Request,
1305                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1306                                            &key,
1307                                            NULL);
1308 
1309     if (NT_SUCCESS(status))
1310     {
1311         if((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY)) ||
1312            (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != key->KeyLength))
1313         {
1314 
1315             //
1316             // Key is too small to have a header or the key length doesn't
1317             // match the input buffer length.  Key must be invalid
1318             //
1319 
1320             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
1321                         "RequestValidateDvdSendKey: [%p] IOCTL_DVD_SEND_KEY - "
1322                         "key is too small or does not match KeyLength\n",
1323                         Request));
1324             status = STATUS_INVALID_PARAMETER;
1325         }
1326     }
1327 
1328     if (NT_SUCCESS(status))
1329     {
1330         // allow only certain key type (non-destructive) to go through
1331         // IOCTL_DVD_SEND_KEY (which only requires READ access to the device)
1332         if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_SEND_KEY)
1333         {
1334             if ((key->KeyType != DvdChallengeKey) &&
1335                 (key->KeyType != DvdBusKey2) &&
1336                 (key->KeyType != DvdInvalidateAGID))
1337             {
1338                 status = STATUS_INVALID_PARAMETER;
1339             }
1340         }
1341         else if ((key->KeyType != DvdChallengeKey) &&
1342                 (key->KeyType != DvdBusKey1) &&
1343                 (key->KeyType != DvdBusKey2) &&
1344                 (key->KeyType != DvdTitleKey) &&
1345                 (key->KeyType != DvdAsf) &&
1346                 (key->KeyType != DvdSetRpcKey) &&
1347                 (key->KeyType != DvdGetRpcKey) &&
1348                 (key->KeyType != DvdDiskKey) &&
1349                 (key->KeyType != DvdInvalidateAGID))
1350         {
1351             status = STATUS_INVALID_PARAMETER;
1352         }
1353         else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
1354         {
1355             // reject the request if it's not a DVD device.
1356             status = STATUS_INVALID_DEVICE_REQUEST;
1357         }
1358     }
1359 
1360     return status;
1361 }
1362 
1363 NTSTATUS
RequestValidateGetConfiguration(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1364 RequestValidateGetConfiguration(
1365     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1366     _In_  WDFREQUEST               Request,
1367     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1368     _Out_ size_t *                 DataLength
1369     )
1370 /*++
1371 
1372 Routine Description:
1373 
1374    Validate request of IOCTL_CDROM_GET_CONFIGURATION
1375 
1376 Arguments:
1377 
1378     DeviceExtension - device context
1379     Request - request to be handled
1380     RequestParameters - request parameter
1381     DataLength - transferred data length
1382 
1383 Return Value:
1384 
1385     NTSTATUS
1386 
1387 --*/
1388 {
1389     NTSTATUS    status = STATUS_SUCCESS;
1390 
1391     *DataLength = 0;
1392 
1393     if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
1394         sizeof(GET_CONFIGURATION_HEADER))
1395     {
1396         status = STATUS_BUFFER_TOO_SMALL;
1397         *DataLength = sizeof(GET_CONFIGURATION_HEADER);
1398     }
1399     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > 0xffff)
1400     {
1401         // output buffer is too large
1402         status = STATUS_INVALID_BUFFER_SIZE;
1403     }
1404     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
1405         DeviceExtension->AdapterDescriptor->AlignmentMask)
1406     {
1407         // buffer is not proper size multiple
1408         status = STATUS_INVALID_PARAMETER;
1409     }
1410 
1411     if (NT_SUCCESS(status))
1412     {
1413 
1414 #if BUILD_WOW64_ENABLED && defined(_WIN64)
1415 
1416         if (WdfRequestIsFrom32BitProcess(Request))
1417         {
1418             PGET_CONFIGURATION_IOCTL_INPUT32 inputBuffer = NULL;
1419 
1420             if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
1421                 sizeof(GET_CONFIGURATION_IOCTL_INPUT32))
1422             {
1423                 status = STATUS_INFO_LENGTH_MISMATCH;
1424             }
1425 
1426             //
1427             // also verify the arguments are reasonable.
1428             //
1429             if (NT_SUCCESS(status))
1430             {
1431                 status = WdfRequestRetrieveInputBuffer(Request,
1432                                                        RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1433                                                        &inputBuffer,
1434                                                        NULL);
1435             }
1436 
1437             if (NT_SUCCESS(status))
1438             {
1439                 if (inputBuffer->Feature > 0xffff)
1440                 {
1441                     status = STATUS_INVALID_PARAMETER;
1442                 }
1443                 else if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
1444                          (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) &&
1445                          (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL))
1446                 {
1447                     status = STATUS_INVALID_PARAMETER;
1448                 }
1449                 else if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1])
1450                 {
1451                     status = STATUS_INVALID_PARAMETER;
1452                 }
1453             }
1454         }
1455         else
1456 
1457 #endif
1458 
1459         {
1460             PGET_CONFIGURATION_IOCTL_INPUT inputBuffer = NULL;
1461 
1462             if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
1463                 sizeof(GET_CONFIGURATION_IOCTL_INPUT))
1464             {
1465                 status = STATUS_INFO_LENGTH_MISMATCH;
1466             }
1467 
1468             // also verify the arguments are reasonable.
1469             if (NT_SUCCESS(status))
1470             {
1471                 status = WdfRequestRetrieveInputBuffer(Request,
1472                                                        RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1473                                                        &inputBuffer,
1474                                                        NULL);
1475             }
1476 
1477             if (NT_SUCCESS(status))
1478             {
1479                 if (inputBuffer->Feature > 0xffff)
1480                 {
1481                     status = STATUS_INVALID_PARAMETER;
1482                 }
1483                 else if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
1484                          (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) &&
1485                          (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL))
1486                 {
1487                     status = STATUS_INVALID_PARAMETER;
1488                 }
1489                 else if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1])
1490                 {
1491                     status = STATUS_INVALID_PARAMETER;
1492                 }
1493             }
1494         }
1495     }
1496 
1497     return status;
1498 }
1499 
1500 NTSTATUS
RequestValidateSetSpeed(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1501 RequestValidateSetSpeed(
1502     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1503     _In_  WDFREQUEST               Request,
1504     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1505     _Out_ size_t *                 DataLength
1506     )
1507 /*++
1508 
1509 Routine Description:
1510 
1511    Validate request of IOCTL_CDROM_SET_SPEED
1512 
1513 Arguments:
1514 
1515     DeviceExtension - device context
1516     Request - request to be handled
1517     RequestParameters - request parameter
1518     DataLength - transferred data length
1519 
1520 Return Value:
1521 
1522     NTSTATUS
1523 
1524 --*/
1525 {
1526     NTSTATUS                status = STATUS_SUCCESS;
1527     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
1528     PCDROM_SET_SPEED        inputBuffer = NULL;
1529     ULONG                   requiredLength = 0;
1530 
1531     *DataLength = 0;
1532 
1533     if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SET_SPEED))
1534     {
1535         status = STATUS_INFO_LENGTH_MISMATCH;
1536     }
1537 
1538     if (NT_SUCCESS(status))
1539     {
1540         // Get the request type using CDROM_SET_SPEED structure
1541         status = WdfRequestRetrieveInputBuffer(Request,
1542                                                sizeof(CDROM_SET_SPEED),
1543                                                &inputBuffer,
1544                                                NULL);
1545 
1546     }
1547 
1548     if (NT_SUCCESS(status))
1549     {
1550         if (inputBuffer->RequestType > CdromSetStreaming)
1551         {
1552             // Unknown request type.
1553             status = STATUS_INVALID_PARAMETER;
1554         }
1555         else if (inputBuffer->RequestType == CdromSetSpeed)
1556         {
1557             requiredLength = sizeof(CDROM_SET_SPEED);
1558         }
1559         else
1560         {
1561             // Don't send SET STREAMING command if this is not a MMC compliant device
1562             if (cdData->Mmc.IsMmc == FALSE)
1563             {
1564                 status = STATUS_INVALID_DEVICE_REQUEST;
1565             }
1566 
1567             requiredLength = sizeof(CDROM_SET_STREAMING);
1568         }
1569     }
1570 
1571     if (NT_SUCCESS(status))
1572     {
1573         if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < requiredLength)
1574         {
1575             // Input buffer too small
1576             status = STATUS_INFO_LENGTH_MISMATCH;
1577         }
1578     }
1579 
1580     return status;
1581 }
1582 
1583 NTSTATUS
RequestValidateAacsReadMediaKeyBlock(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1584 RequestValidateAacsReadMediaKeyBlock(
1585     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1586     _In_  WDFREQUEST               Request,
1587     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1588     _Out_ size_t *                 DataLength
1589     )
1590 /*++
1591 
1592 Routine Description:
1593 
1594    Validate request of IOCTL_AACS_READ_MEDIA_KEY_BLOCK
1595 
1596 Arguments:
1597 
1598     DeviceExtension - device context
1599     Request - request to be handled
1600     RequestParameters - request parameter
1601     DataLength - transferred data length
1602 
1603 Return Value:
1604 
1605     NTSTATUS
1606 
1607 --*/
1608 {
1609     NTSTATUS                status = STATUS_SUCCESS;
1610     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
1611     PAACS_LAYER_NUMBER      layerNumber = NULL;
1612 
1613     *DataLength = 0;
1614 
1615     if (!cdData->Mmc.IsAACS)
1616     {
1617         status = STATUS_INVALID_DEVICE_REQUEST;
1618     }
1619     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_LAYER_NUMBER))
1620     {
1621         status = STATUS_INFO_LENGTH_MISMATCH;
1622     }
1623     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < 8)
1624     {
1625         // This is a variable-length structure, but we're pretty sure
1626         // it can never be less than eight bytes...
1627         *DataLength = 8;
1628         status = STATUS_BUFFER_TOO_SMALL;
1629     }
1630 
1631     if (NT_SUCCESS(status))
1632     {
1633         status = WdfRequestRetrieveInputBuffer(Request,
1634                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1635                                                &layerNumber,
1636                                                NULL);
1637     }
1638 
1639     if (NT_SUCCESS(status))
1640     {
1641         if (*layerNumber > 255)
1642         {
1643             status = STATUS_INVALID_PARAMETER;
1644         }
1645     }
1646 
1647     return status;
1648 }
1649 
1650 NTSTATUS
RequestValidateAacsStartSession(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1651 RequestValidateAacsStartSession(
1652     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1653     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1654     _Out_ size_t *                 DataLength
1655     )
1656 /*++
1657 
1658 Routine Description:
1659 
1660    Validate request of IOCTL_AACS_START_SESSION
1661 
1662 Arguments:
1663 
1664     DeviceExtension - device context
1665     RequestParameters - request parameter
1666     DataLength - transferred data length
1667 
1668 Return Value:
1669 
1670     NTSTATUS
1671 
1672 --*/
1673 {
1674     NTSTATUS                status = STATUS_SUCCESS;
1675     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
1676 
1677     *DataLength = 0;
1678 
1679     if (!cdData->Mmc.IsAACS)
1680     {
1681         status = STATUS_INVALID_DEVICE_REQUEST;
1682     }
1683     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(DVD_SESSION_ID))
1684     {
1685         *DataLength = sizeof(DVD_SESSION_ID);
1686         status = STATUS_BUFFER_TOO_SMALL;
1687     }
1688     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(DVD_SESSION_ID))
1689     {
1690         status = STATUS_INVALID_BUFFER_SIZE;
1691     }
1692 
1693     return status;
1694 }
1695 
1696 NTSTATUS
RequestValidateAacsSendCertificate(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1697 RequestValidateAacsSendCertificate(
1698     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1699     _In_  WDFREQUEST               Request,
1700     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1701     _Out_ size_t *                 DataLength
1702     )
1703 /*++
1704 
1705 Routine Description:
1706 
1707    Validate request of IOCTL_AACS_SEND_CERTIFICATE
1708 
1709 Arguments:
1710 
1711     DeviceExtension - device context
1712     Request - request to be handled
1713     RequestParameters - request parameter
1714     DataLength - transferred data length
1715 
1716 Return Value:
1717 
1718     NTSTATUS
1719 
1720 --*/
1721 {
1722     NTSTATUS                status = STATUS_SUCCESS;
1723     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
1724     PAACS_SEND_CERTIFICATE  inputBuffer = NULL;
1725 
1726     *DataLength = 0;
1727 
1728     if (!cdData->Mmc.IsAACS)
1729     {
1730         status = STATUS_INVALID_DEVICE_REQUEST;
1731     }
1732     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_SEND_CERTIFICATE))
1733     {
1734         status = STATUS_INFO_LENGTH_MISMATCH;
1735     }
1736 
1737     if (NT_SUCCESS(status))
1738     {
1739         status = WdfRequestRetrieveInputBuffer(Request,
1740                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1741                                                &inputBuffer,
1742                                                NULL);
1743     }
1744 
1745     if (NT_SUCCESS(status))
1746     {
1747         if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
1748         {
1749             status = STATUS_INVALID_PARAMETER;
1750         }
1751     }
1752 
1753     return status;
1754 }
1755 
1756 NTSTATUS
RequestValidateAacsGetCertificate(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1757 RequestValidateAacsGetCertificate(
1758     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1759     _In_  WDFREQUEST               Request,
1760     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1761     _Out_ size_t *                 DataLength
1762     )
1763 /*++
1764 
1765 Routine Description:
1766 
1767    Validate request of IOCTL_AACS_GET_CERTIFICATE
1768 
1769 Arguments:
1770 
1771     DeviceExtension - device context
1772     Request - request to be handled
1773     RequestParameters - request parameter
1774     DataLength - transferred data length
1775 
1776 Return Value:
1777 
1778     NTSTATUS
1779 
1780 --*/
1781 {
1782     NTSTATUS                status = STATUS_SUCCESS;
1783     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
1784     PDVD_SESSION_ID         sessionId = NULL;
1785 
1786     *DataLength = 0;
1787 
1788     if (!cdData->Mmc.IsAACS)
1789     {
1790         status = STATUS_INVALID_DEVICE_REQUEST;
1791     }
1792     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
1793     {
1794         status = STATUS_INFO_LENGTH_MISMATCH;
1795     }
1796     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_CERTIFICATE))
1797     {
1798         *DataLength = sizeof(AACS_CERTIFICATE);
1799         status = STATUS_BUFFER_TOO_SMALL;
1800     }
1801     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_CERTIFICATE))
1802     {
1803         status = STATUS_INVALID_BUFFER_SIZE;
1804     }
1805 
1806     if (NT_SUCCESS(status))
1807     {
1808         status = WdfRequestRetrieveInputBuffer(Request,
1809                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1810                                                &sessionId,
1811                                                NULL);
1812     }
1813 
1814     if (NT_SUCCESS(status))
1815     {
1816         if (*sessionId > MAX_COPY_PROTECT_AGID)
1817         {
1818             status = STATUS_INVALID_PARAMETER;
1819         }
1820     }
1821 
1822     return status;
1823 }
1824 
1825 NTSTATUS
RequestValidateAacsGetChallengeKey(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1826 RequestValidateAacsGetChallengeKey(
1827     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1828     _In_  WDFREQUEST               Request,
1829     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1830     _Out_ size_t *                 DataLength
1831     )
1832 /*++
1833 
1834 Routine Description:
1835 
1836    Validate request of IOCTL_AACS_GET_CHALLENGE_KEY
1837 
1838 Arguments:
1839 
1840     DeviceExtension - device context
1841     Request - request to be handled
1842     RequestParameters - request parameter
1843     DataLength - transferred data length
1844 
1845 Return Value:
1846 
1847     NTSTATUS
1848 
1849 --*/
1850 {
1851     NTSTATUS                status = STATUS_SUCCESS;
1852     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
1853     PDVD_SESSION_ID         sessionId = NULL;
1854 
1855     *DataLength = 0;
1856 
1857     if (!cdData->Mmc.IsAACS)
1858     {
1859         status = STATUS_INVALID_DEVICE_REQUEST;
1860     }
1861     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
1862     {
1863         status = STATUS_INFO_LENGTH_MISMATCH;
1864     }
1865     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_CHALLENGE_KEY))
1866     {
1867         *DataLength = sizeof(AACS_CHALLENGE_KEY);
1868         status = STATUS_BUFFER_TOO_SMALL;
1869     }
1870     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_CHALLENGE_KEY))
1871     {
1872         status = STATUS_INVALID_BUFFER_SIZE;
1873     }
1874 
1875     if (NT_SUCCESS(status))
1876     {
1877         status = WdfRequestRetrieveInputBuffer(Request,
1878                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1879                                                &sessionId,
1880                                                NULL);
1881     }
1882 
1883     if (NT_SUCCESS(status))
1884     {
1885         if (*sessionId > MAX_COPY_PROTECT_AGID)
1886         {
1887             status = STATUS_INVALID_PARAMETER;
1888         }
1889     }
1890 
1891     return status;
1892 }
1893 
1894 NTSTATUS
RequestValidateAacsSendChallengeKey(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1895 RequestValidateAacsSendChallengeKey(
1896     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1897     _In_  WDFREQUEST               Request,
1898     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1899     _Out_ size_t *                 DataLength
1900     )
1901 /*++
1902 
1903 Routine Description:
1904 
1905    Validate request of IOCTL_AACS_SEND_CHALLENGE_KEY
1906 
1907 Arguments:
1908 
1909     DeviceExtension - device context
1910     Request - request to be handled
1911     RequestParameters - request parameter
1912     DataLength - transferred data length
1913 
1914 Return Value:
1915 
1916     NTSTATUS
1917 
1918 --*/
1919 {
1920     NTSTATUS                    status = STATUS_SUCCESS;
1921     PCDROM_DATA                 cdData = &(DeviceExtension->DeviceAdditionalData);
1922     PAACS_SEND_CHALLENGE_KEY    inputBuffer = NULL;
1923 
1924     *DataLength = 0;
1925 
1926     if (!cdData->Mmc.IsAACS)
1927     {
1928         status = STATUS_INVALID_DEVICE_REQUEST;
1929     }
1930     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_SEND_CHALLENGE_KEY))
1931     {
1932         status = STATUS_INFO_LENGTH_MISMATCH;
1933     }
1934 
1935     if (NT_SUCCESS(status))
1936     {
1937         status = WdfRequestRetrieveInputBuffer(Request,
1938                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
1939                                                &inputBuffer,
1940                                                NULL);
1941     }
1942 
1943     if (NT_SUCCESS(status))
1944     {
1945         if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
1946         {
1947             status = STATUS_INVALID_PARAMETER;
1948         }
1949     }
1950 
1951     return status;
1952 }
1953 
1954 NTSTATUS
RequestValidateAacsReadVolumeId(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)1955 RequestValidateAacsReadVolumeId(
1956     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
1957     _In_  WDFREQUEST               Request,
1958     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
1959     _Out_ size_t *                 DataLength
1960     )
1961 /*++
1962 
1963 Routine Description:
1964 
1965    Validate request of IOCTL_AACS_READ_VOLUME_ID
1966 
1967 Arguments:
1968 
1969     DeviceExtension - device context
1970     Request - request to be handled
1971     RequestParameters - request parameter
1972     DataLength - transferred data length
1973 
1974 Return Value:
1975 
1976     NTSTATUS
1977 
1978 --*/
1979 {
1980     NTSTATUS                status = STATUS_SUCCESS;
1981     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
1982     PDVD_SESSION_ID         sessionId = NULL;
1983 
1984     *DataLength = 0;
1985 
1986     if (!cdData->Mmc.IsAACS)
1987     {
1988         status = STATUS_INVALID_DEVICE_REQUEST;
1989     }
1990     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
1991     {
1992         status = STATUS_INFO_LENGTH_MISMATCH;
1993     }
1994     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_VOLUME_ID))
1995     {
1996         *DataLength = sizeof(AACS_VOLUME_ID);
1997         status = STATUS_BUFFER_TOO_SMALL;
1998     }
1999     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_VOLUME_ID))
2000     {
2001         status = STATUS_INVALID_BUFFER_SIZE;
2002     }
2003 
2004     if (NT_SUCCESS(status))
2005     {
2006         status = WdfRequestRetrieveInputBuffer(Request,
2007                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
2008                                                &sessionId,
2009                                                NULL);
2010     }
2011 
2012     if (NT_SUCCESS(status))
2013     {
2014         if (*sessionId > MAX_COPY_PROTECT_AGID)
2015         {
2016             status = STATUS_INVALID_PARAMETER;
2017         }
2018     }
2019 
2020     return status;
2021 }
2022 
2023 NTSTATUS
RequestValidateAacsReadSerialNumber(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)2024 RequestValidateAacsReadSerialNumber(
2025     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
2026     _In_  WDFREQUEST               Request,
2027     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
2028     _Out_ size_t *                 DataLength
2029     )
2030 /*++
2031 
2032 Routine Description:
2033 
2034    Validate request of IOCTL_AACS_READ_SERIAL_NUMBER
2035 
2036 Arguments:
2037 
2038     DeviceExtension - device context
2039     Request - request to be handled
2040     RequestParameters - request parameter
2041     DataLength - transferred data length
2042 
2043 Return Value:
2044 
2045     NTSTATUS
2046 
2047 --*/
2048 {
2049     NTSTATUS                status = STATUS_SUCCESS;
2050     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
2051     PDVD_SESSION_ID         sessionId = NULL;
2052 
2053     *DataLength = 0;
2054 
2055     if (!cdData->Mmc.IsAACS)
2056     {
2057         status = STATUS_INVALID_DEVICE_REQUEST;
2058     }
2059     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
2060     {
2061         status = STATUS_INFO_LENGTH_MISMATCH;
2062     }
2063     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_SERIAL_NUMBER))
2064     {
2065         *DataLength = sizeof(AACS_SERIAL_NUMBER);
2066         status = STATUS_BUFFER_TOO_SMALL;
2067     }
2068     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_SERIAL_NUMBER))
2069     {
2070         status = STATUS_INVALID_BUFFER_SIZE;
2071     }
2072 
2073     if (NT_SUCCESS(status))
2074     {
2075         status = WdfRequestRetrieveInputBuffer(Request,
2076                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
2077                                                &sessionId,
2078                                                NULL);
2079     }
2080 
2081     if (NT_SUCCESS(status))
2082     {
2083         if (*sessionId > MAX_COPY_PROTECT_AGID)
2084         {
2085             status = STATUS_INVALID_PARAMETER;
2086         }
2087     }
2088 
2089     return status;
2090 }
2091 
2092 NTSTATUS
RequestValidateAacsReadMediaId(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)2093 RequestValidateAacsReadMediaId(
2094     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
2095     _In_  WDFREQUEST               Request,
2096     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
2097     _Out_ size_t *                 DataLength
2098     )
2099 /*++
2100 
2101 Routine Description:
2102 
2103    Validate request of IOCTL_AACS_READ_MEDIA_ID
2104 
2105 Arguments:
2106 
2107     DeviceExtension - device context
2108     Request - request to be handled
2109     RequestParameters - request parameter
2110     DataLength - transferred data length
2111 
2112 Return Value:
2113 
2114     NTSTATUS
2115 
2116 --*/
2117 {
2118     NTSTATUS                status = STATUS_SUCCESS;
2119     PCDROM_DATA             cdData = &(DeviceExtension->DeviceAdditionalData);
2120     PDVD_SESSION_ID         sessionId = NULL;
2121 
2122     *DataLength = 0;
2123 
2124     if (!cdData->Mmc.IsAACS)
2125     {
2126         status = STATUS_INVALID_DEVICE_REQUEST;
2127     }
2128     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
2129     {
2130         status = STATUS_INFO_LENGTH_MISMATCH;
2131     }
2132     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_MEDIA_ID))
2133     {
2134         *DataLength = sizeof(AACS_MEDIA_ID);
2135         status = STATUS_BUFFER_TOO_SMALL;
2136     }
2137     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_MEDIA_ID))
2138     {
2139         status = STATUS_INVALID_BUFFER_SIZE;
2140     }
2141 
2142     if (NT_SUCCESS(status))
2143     {
2144         status = WdfRequestRetrieveInputBuffer(Request,
2145                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
2146                                                &sessionId,
2147                                                NULL);
2148     }
2149 
2150     if (NT_SUCCESS(status))
2151     {
2152         if (*sessionId > MAX_COPY_PROTECT_AGID)
2153         {
2154             status = STATUS_INVALID_PARAMETER;
2155         }
2156     }
2157 
2158     return status;
2159 }
2160 
2161 NTSTATUS
RequestValidateAacsBindingNonce(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)2162 RequestValidateAacsBindingNonce(
2163     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
2164     _In_  WDFREQUEST               Request,
2165     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
2166     _Out_ size_t *                 DataLength
2167     )
2168 /*++
2169 
2170 Routine Description:
2171 
2172    Validate request of IOCTL_AACS_READ_BINDING_NONCE
2173                        IOCTL_AACS_GENERATE_BINDING_NONCE
2174 
2175 Arguments:
2176 
2177     DeviceExtension - device context
2178     Request - request to be handled
2179     RequestParameters - request parameter
2180     DataLength - transferred data length
2181 
2182 Return Value:
2183 
2184     NTSTATUS
2185 
2186 --*/
2187 {
2188     NTSTATUS                 status = STATUS_SUCCESS;
2189     PCDROM_DATA              cdData = &(DeviceExtension->DeviceAdditionalData);
2190     PAACS_READ_BINDING_NONCE inputBuffer = NULL;
2191 
2192     *DataLength = 0;
2193 
2194     if (!cdData->Mmc.IsAACS)
2195     {
2196         status = STATUS_INVALID_DEVICE_REQUEST;
2197     }
2198     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_READ_BINDING_NONCE))
2199     {
2200         status = STATUS_INFO_LENGTH_MISMATCH;
2201     }
2202     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_BINDING_NONCE))
2203     {
2204         *DataLength = sizeof(AACS_BINDING_NONCE);
2205         status = STATUS_BUFFER_TOO_SMALL;
2206     }
2207     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_BINDING_NONCE))
2208     {
2209         status = STATUS_INVALID_BUFFER_SIZE;
2210     }
2211 
2212     if (NT_SUCCESS(status))
2213     {
2214         status = WdfRequestRetrieveInputBuffer(Request,
2215                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
2216                                                &inputBuffer,
2217                                                NULL);
2218     }
2219 
2220     if (NT_SUCCESS(status))
2221     {
2222         if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID)
2223         {
2224             status = STATUS_INVALID_PARAMETER;
2225         }
2226         else if (inputBuffer->NumberOfSectors > 255)
2227         {
2228             status = STATUS_INVALID_PARAMETER;
2229         }
2230         else if (inputBuffer->StartLba > MAXULONG)
2231         {
2232             status = STATUS_INVALID_PARAMETER;
2233         }
2234     }
2235 
2236     return status;
2237 }
2238 
2239 NTSTATUS
RequestValidateExclusiveAccess(_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)2240 RequestValidateExclusiveAccess(
2241     _In_  WDFREQUEST               Request,
2242     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
2243     _Out_ size_t *                 DataLength
2244     )
2245 /*++
2246 
2247 Routine Description:
2248 
2249    Validate request of IOCTL_CDROM_EXCLUSIVE_ACCESS
2250 
2251 Arguments:
2252 
2253     Request - request to be handled
2254     RequestParameters - request parameter
2255     DataLength - transferred data length
2256 
2257 Return Value:
2258 
2259     NTSTATUS
2260 
2261 --*/
2262 {
2263     NTSTATUS                status = STATUS_SUCCESS;
2264     PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL;
2265 
2266     *DataLength = 0;
2267 
2268     if (KeGetCurrentIrql() != PASSIVE_LEVEL)
2269     {
2270         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: IOCTL must be called at passive level.\n"));
2271         status = STATUS_INVALID_DEVICE_REQUEST;
2272     }
2273     else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_EXCLUSIVE_ACCESS))
2274     {
2275 
2276         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Input buffer too small\n"));
2277         status = STATUS_INFO_LENGTH_MISMATCH;
2278     }
2279 
2280     if (NT_SUCCESS(status))
2281     {
2282         status = WdfRequestRetrieveInputBuffer(Request,
2283                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
2284                                                &exclusiveAccess,
2285                                                NULL);
2286     }
2287 
2288     if (NT_SUCCESS(status))
2289     {
2290         switch (exclusiveAccess->RequestType)
2291         {
2292             case ExclusiveAccessQueryState:
2293             {
2294                 if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
2295                     sizeof(CDROM_EXCLUSIVE_LOCK_STATE))
2296                 {
2297                     //
2298                     // Output buffer too small.
2299                     //
2300                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Output buffer too small\n"));
2301                     *DataLength = sizeof(CDROM_EXCLUSIVE_LOCK_STATE);
2302                     status = STATUS_BUFFER_TOO_SMALL;
2303                 }
2304                 break;
2305             }
2306 
2307             case ExclusiveAccessLockDevice:
2308             {
2309                 if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
2310                     sizeof(CDROM_EXCLUSIVE_LOCK))
2311                 {
2312                     //
2313                     // Input buffer too small
2314                     //
2315                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Input buffer too small\n"));
2316                     status = STATUS_INFO_LENGTH_MISMATCH;
2317                 }
2318                 break;
2319             }
2320             case ExclusiveAccessUnlockDevice:
2321             {
2322                 //
2323                 // Nothing to check
2324                 //
2325                 break;
2326             }
2327 
2328             default:
2329             {
2330                 //
2331                 // Unknown request type.
2332                 //
2333                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Invalid request type\n"));
2334                 status = STATUS_INVALID_PARAMETER;
2335             }
2336         }
2337     }
2338 
2339     return status;
2340 }
2341 
_IRQL_requires_max_(APC_LEVEL)2342 _IRQL_requires_max_(APC_LEVEL)
2343 NTSTATUS
2344 RequestHandleExclusiveAccessQueryLockState(
2345     _In_ WDFDEVICE Device,
2346     _In_ WDFREQUEST Request
2347     )
2348 /*++
2349 
2350 Routine Description:
2351 
2352    Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessQueryState
2353 
2354 Arguments:
2355 
2356     DeviceExtension - device context
2357     Request - request to be handled
2358     RequestParameters - request parameter
2359     DataLength - transferred data length
2360 
2361 Return Value:
2362 
2363     NTSTATUS
2364 
2365 --*/
2366 {
2367     NTSTATUS                    status = STATUS_SUCCESS;
2368     PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
2369     PCDROM_DATA                 cdData = &deviceExtension->DeviceAdditionalData;
2370     PCDROM_EXCLUSIVE_LOCK_STATE exclusiveLockState = NULL;
2371 
2372     PAGED_CODE();
2373 
2374     status = WdfRequestRetrieveOutputBuffer(Request,
2375                                             sizeof(CDROM_EXCLUSIVE_LOCK_STATE),
2376                                             &exclusiveLockState,
2377                                             NULL);
2378     NT_ASSERT(NT_SUCCESS(status));
2379 
2380     RtlZeroMemory(exclusiveLockState, sizeof(CDROM_EXCLUSIVE_LOCK_STATE));
2381 
2382     if (EXCLUSIVE_MODE(cdData))
2383     {
2384         // Device is locked for exclusive use
2385         exclusiveLockState->LockState = TRUE;
2386 
2387         RtlCopyMemory(&exclusiveLockState->CallerName,
2388                       &cdData->CallerName,
2389                       CDROM_EXCLUSIVE_CALLER_LENGTH);
2390 
2391     }
2392     else
2393     {
2394         // Device is not locked
2395         exclusiveLockState->LockState = FALSE;
2396     }
2397 
2398     RequestCompletion(deviceExtension, Request, status, sizeof(CDROM_EXCLUSIVE_LOCK_STATE));
2399 
2400     return status;
2401 }
2402 
_IRQL_requires_max_(APC_LEVEL)2403 _IRQL_requires_max_(APC_LEVEL)
2404 NTSTATUS
2405 RequestHandleExclusiveAccessLockDevice(
2406     _In_ WDFDEVICE Device,
2407     _In_ WDFREQUEST Request
2408     )
2409 /*++
2410 
2411 Routine Description:
2412 
2413    Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessLockDevice
2414 
2415 Arguments:
2416 
2417     DeviceExtension - device context
2418     Request - request to be handled
2419     RequestParameters - request parameter
2420     DataLength - transferred data length
2421 
2422 Return Value:
2423 
2424     NTSTATUS
2425 
2426 --*/
2427 {
2428     NTSTATUS                status = STATUS_SUCCESS;
2429     PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
2430     PCDROM_DATA             cdData = &deviceExtension->DeviceAdditionalData;
2431     PCDROM_EXCLUSIVE_LOCK   exclusiveLock = NULL;
2432     PIO_ERROR_LOG_PACKET    logEntry;
2433 
2434     WDFFILEOBJECT           fileObject = NULL;
2435     ULONG                   idx = 0;
2436     ULONG                   nameLength = 0;
2437 
2438     PAGED_CODE();
2439 
2440     fileObject = WdfRequestGetFileObject(Request);
2441 
2442     if (fileObject == NULL)
2443     {
2444         status = STATUS_INVALID_HANDLE;
2445 
2446         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
2447                     "RequestHandleExclusiveAccessLockDevice: FileObject is NULL, cannot grant exclusive access\n"));
2448     }
2449 
2450     if (NT_SUCCESS(status))
2451     {
2452         status = WdfRequestRetrieveInputBuffer(Request,
2453                                                sizeof(CDROM_EXCLUSIVE_LOCK),
2454                                                &exclusiveLock,
2455                                                NULL);
2456     }
2457 
2458     if (NT_SUCCESS(status))
2459     {
2460         // Validate the caller name string
2461         for (idx = 0; (idx < CDROM_EXCLUSIVE_CALLER_LENGTH) && (exclusiveLock->CallerName[idx] != '\0'); idx++)
2462         {
2463             if (!ValidChar(exclusiveLock->CallerName[idx]))
2464             {
2465                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
2466                             "RequestHandleExclusiveAccessLockDevice: Invalid characters in caller name\n"));
2467                 // error out
2468                 status = STATUS_INVALID_PARAMETER;
2469                 break;
2470             }
2471         }
2472     }
2473 
2474     if (NT_SUCCESS(status))
2475     {
2476         if ((idx == 0) || (idx >= CDROM_EXCLUSIVE_CALLER_LENGTH))
2477         {
2478 
2479             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
2480                         "RequestHandleExclusiveAccessLockDevice: Not a valid null terminated string.\n"));
2481             //error out
2482             status = STATUS_INVALID_PARAMETER;
2483         }
2484         else
2485         {
2486             nameLength = idx+1; // Add 1 for the NULL character
2487             NT_ASSERT(nameLength <= CDROM_EXCLUSIVE_CALLER_LENGTH);
2488         }
2489     }
2490 
2491     // If the file system is still mounted on this device fail the request,
2492     // unless the force lock flag is set.
2493     if (NT_SUCCESS(status))
2494     {
2495         if ((TEST_FLAG(exclusiveLock->Access.Flags, CDROM_LOCK_IGNORE_VOLUME) == FALSE) &&
2496             IsVolumeMounted(deviceExtension->DeviceObject))
2497         {
2498                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
2499                             "RequestHandleExclusiveAccessLockDevice: Unable to lock device, file system mounted\n"));
2500                 status = STATUS_INVALID_DEVICE_STATE;
2501         }
2502     }
2503 
2504     // Lock the device for exclusive access if the device is not already locked
2505     if (NT_SUCCESS(status))
2506     {
2507         if (InterlockedCompareExchangePointer((PVOID)&cdData->ExclusiveOwner, (PVOID)fileObject, NULL) == NULL)
2508         {
2509             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
2510                         "RequestHandleExclusiveAccessLockDevice: Entering exclusive mode! Device locked by file object %p\n", fileObject));
2511 
2512             // Zero out the CallerName before storing it in the extension
2513             RtlZeroMemory(&cdData->CallerName, CDROM_EXCLUSIVE_CALLER_LENGTH);
2514             RtlCopyMemory(&cdData->CallerName,
2515                           &exclusiveLock->CallerName,
2516                           nameLength);
2517 
2518             // Send Exclusive Lock notification
2519             DeviceSendNotification(deviceExtension,
2520                                    &GUID_IO_CDROM_EXCLUSIVE_LOCK,
2521                                    0,
2522                                    NULL);
2523 
2524             // Log an informational event with the caller name
2525             logEntry = IoAllocateErrorLogEntry(
2526                                 deviceExtension->DeviceObject,
2527                                 sizeof(IO_ERROR_LOG_PACKET) + CDROM_EXCLUSIVE_CALLER_LENGTH);
2528 
2529             if (logEntry != NULL)
2530             {
2531                 PUCHAR dumpDataPtr = (PUCHAR) logEntry->DumpData;
2532 
2533                 logEntry->FinalStatus       = STATUS_SUCCESS;
2534                 logEntry->ErrorCode         = IO_CDROM_EXCLUSIVE_LOCK;
2535                 logEntry->SequenceNumber    = 0;
2536                 logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL;
2537                 logEntry->IoControlCode     = IOCTL_CDROM_EXCLUSIVE_ACCESS;
2538                 logEntry->RetryCount        = 0;
2539                 logEntry->UniqueErrorValue  = 0x1;
2540                 logEntry->DumpDataSize      = CDROM_EXCLUSIVE_CALLER_LENGTH;
2541 
2542                 RtlCopyMemory(dumpDataPtr,
2543                                 (PUCHAR)&cdData->CallerName,
2544                                 CDROM_EXCLUSIVE_CALLER_LENGTH);
2545 
2546                 // Write the error log packet.
2547                 IoWriteErrorLogEntry(logEntry);
2548             }
2549 
2550         }
2551         else
2552         {
2553             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
2554                         "RequestHandleExclusiveAccessLockDevice: Unable to lock device, device already locked.\n"));
2555 
2556             status = STATUS_ACCESS_DENIED;
2557         }
2558     }
2559 
2560     RequestCompletion(deviceExtension, Request, status, 0);
2561 
2562     return status;
2563 }
2564 
_IRQL_requires_max_(APC_LEVEL)2565 _IRQL_requires_max_(APC_LEVEL)
2566 NTSTATUS
2567 RequestHandleExclusiveAccessUnlockDevice(
2568     _In_ WDFDEVICE Device,
2569     _In_ WDFREQUEST Request
2570     )
2571 /*++
2572 
2573 Routine Description:
2574 
2575    Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessUnlockDevice
2576 
2577 Arguments:
2578 
2579     Device - device handle
2580     Request - request to be handled
2581 
2582 Return Value:
2583 
2584     NTSTATUS
2585 
2586 --*/
2587 {
2588     NTSTATUS                status = STATUS_SUCCESS;
2589     PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
2590     PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL;
2591     WDFFILEOBJECT           fileObject = NULL;
2592 
2593     PAGED_CODE();
2594 
2595     fileObject = WdfRequestGetFileObject(Request);
2596 
2597     if (fileObject == NULL)
2598     {
2599         // The device can be unlocked from exclusive mode only via the file object which locked it.
2600         status = STATUS_INVALID_HANDLE;
2601 
2602         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
2603                     "RequestHandleExclusiveAccessUnlockDevice: FileObject is NULL, cannot release exclusive access\n"));
2604     }
2605 
2606     if (NT_SUCCESS(status))
2607     {
2608         status = WdfRequestRetrieveInputBuffer(Request,
2609                                                sizeof(PCDROM_EXCLUSIVE_ACCESS),
2610                                                &exclusiveAccess,
2611                                                NULL);
2612     }
2613 
2614     if (NT_SUCCESS(status))
2615     {
2616         status = DeviceUnlockExclusive(deviceExtension, fileObject,
2617                         TEST_FLAG(exclusiveAccess->Flags, CDROM_NO_MEDIA_NOTIFICATIONS));
2618 
2619         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessUnlockDevice: Device unlocked\n"));
2620     }
2621 
2622     RequestCompletion(deviceExtension, Request, status, 0);
2623 
2624     return status;
2625 }
2626 
2627 NTSTATUS
RequestHandleQueryPropertyRetrieveCachedData(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)2628 RequestHandleQueryPropertyRetrieveCachedData(
2629     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
2630     _In_  WDFREQUEST               Request,
2631     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
2632     _Out_ size_t *                 DataLength
2633     )
2634 /*++
2635 
2636 Routine Description:
2637 
2638    Handle request of IOCTL_STORAGE_QUERY_PROPERTY when the required data is cached.
2639 
2640 Arguments:
2641 
2642     DeviceExtension - device context
2643     Request - request to be handled
2644     RequestParameters - request parameter
2645     DataLength - transferred data length
2646 
2647 Return Value:
2648 
2649     NTSTATUS
2650 
2651 --*/
2652 {
2653     NTSTATUS                status = STATUS_SUCCESS;
2654     PSTORAGE_PROPERTY_QUERY inputBuffer = NULL;
2655 
2656     *DataLength = 0;
2657 
2658     status = WdfRequestRetrieveInputBuffer(Request,
2659                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
2660                                            &inputBuffer,
2661                                            NULL);
2662 
2663     if (NT_SUCCESS(status))
2664     {
2665         if (inputBuffer->PropertyId == StorageDeviceProperty)
2666         {
2667             // check output buffer length
2668             if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
2669             {
2670                 // According to MSDN, an output buffer of size 0 can be used to determine if a property exists
2671                 // so this must be a success case with no data transferred
2672                 *DataLength = 0;
2673                 status = STATUS_SUCCESS;
2674             }
2675             else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER))
2676             {
2677                 // Buffer too small
2678                 *DataLength = DeviceExtension->DeviceDescriptor->Size;
2679                 status = STATUS_BUFFER_TOO_SMALL;
2680             }
2681             else
2682             {
2683                 PSTORAGE_DEVICE_DESCRIPTOR  outputDescriptor = NULL;
2684                 CHAR*                       localDescriptorBuffer = (CHAR*)DeviceExtension->DeviceDescriptor;
2685 
2686                 status = WdfRequestRetrieveOutputBuffer(Request,
2687                                                         RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
2688                                                         &outputDescriptor,
2689                                                         NULL);
2690 
2691                 if (NT_SUCCESS(status))
2692                 {
2693                     // transfer as much data out as the buffer will allow
2694                     *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
2695                                       DeviceExtension->DeviceDescriptor->Size);
2696 
2697                     RtlCopyMemory(outputDescriptor,
2698                                   DeviceExtension->DeviceDescriptor,
2699                                   *DataLength);
2700 
2701                     // walk through and update offset variables to reflect data that didn't make it into the output buffer
2702                     if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, VendorIdOffset)) &&
2703                         (DeviceExtension->DeviceDescriptor->VendorIdOffset != 0) &&
2704                         (DeviceExtension->DeviceDescriptor->VendorIdOffset != 0xFFFFFFFF))
2705                     {
2706                         // set VendorIdOffset appropriately
2707                         if (*DataLength <
2708                             (DeviceExtension->DeviceDescriptor->VendorIdOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->VendorIdOffset)))
2709                         {
2710                             outputDescriptor->VendorIdOffset = 0;
2711                         }
2712                     }
2713 
2714                     if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, ProductIdOffset)) &&
2715                         (DeviceExtension->DeviceDescriptor->ProductIdOffset != 0) &&
2716                         (DeviceExtension->DeviceDescriptor->ProductIdOffset != 0xFFFFFFFF))
2717                     {
2718                         // set ProductIdOffset appropriately
2719                         if (*DataLength <
2720                             (DeviceExtension->DeviceDescriptor->ProductIdOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->ProductIdOffset)))
2721                         {
2722                             outputDescriptor->ProductIdOffset = 0;
2723                         }
2724                     }
2725 
2726                     if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, ProductRevisionOffset)) &&
2727                         (DeviceExtension->DeviceDescriptor->ProductRevisionOffset != 0) &&
2728                         (DeviceExtension->DeviceDescriptor->ProductRevisionOffset != 0xFFFFFFFF))
2729                     {
2730                         // set ProductRevisionOffset appropriately
2731                         if (*DataLength <
2732                             (DeviceExtension->DeviceDescriptor->ProductRevisionOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->ProductRevisionOffset)))
2733                         {
2734                             outputDescriptor->ProductRevisionOffset = 0;
2735                         }
2736                     }
2737 
2738                     if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, SerialNumberOffset)) &&
2739                         (DeviceExtension->DeviceDescriptor->SerialNumberOffset != 0) &&
2740                         (DeviceExtension->DeviceDescriptor->SerialNumberOffset != 0xFFFFFFFF))
2741                     {
2742                         // set SerialNumberOffset appropriately
2743                         if (*DataLength <
2744                             (DeviceExtension->DeviceDescriptor->SerialNumberOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->SerialNumberOffset)))
2745                         {
2746                             // NOTE: setting this to 0 since that is what most port drivers do
2747                             //       [this could cause issues with SCSI port devices whose clients expect -1 in this field]
2748                             outputDescriptor->SerialNumberOffset = 0;
2749                         }
2750                     }
2751                     status = STATUS_SUCCESS;
2752                 }
2753             }
2754         }   //end of StorageDeviceProperty
2755         else if (inputBuffer->PropertyId == StorageAdapterProperty)
2756         {
2757             if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0)
2758             {
2759                 // According to MSDN, an output buffer of size 0 can be used to determine if a property exists
2760                 // so this must be a success case with no data transferred
2761                 *DataLength = 0;
2762                 status = STATUS_SUCCESS;
2763             }
2764             else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER))
2765             {
2766                 // Buffer too small
2767                 *DataLength = DeviceExtension->AdapterDescriptor->Size;
2768                 status = STATUS_BUFFER_TOO_SMALL;
2769             }
2770             else
2771             {
2772                 PSTORAGE_ADAPTER_DESCRIPTOR outputDescriptor = NULL;
2773 
2774                 status = WdfRequestRetrieveOutputBuffer(Request,
2775                                                         RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
2776                                                         &outputDescriptor,
2777                                                         NULL);
2778                 if (NT_SUCCESS(status))
2779                 {
2780                     // copy as much data out as the buffer will allow
2781                     *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
2782                                       DeviceExtension->AdapterDescriptor->Size);
2783 
2784                     RtlCopyMemory(outputDescriptor,
2785                                   DeviceExtension->AdapterDescriptor,
2786                                   *DataLength);
2787 
2788                     // set status
2789                     status = STATUS_SUCCESS;
2790                 }
2791             }
2792         }
2793     }
2794 
2795     return status;
2796 }
2797 
2798 NTSTATUS
RequestHandleQueryPropertyDeviceUniqueId(_In_ WDFDEVICE Device,_In_ WDFREQUEST Request)2799 RequestHandleQueryPropertyDeviceUniqueId(
2800     _In_ WDFDEVICE        Device,
2801     _In_ WDFREQUEST       Request
2802     )
2803 /*++
2804 
2805 Routine Description:
2806 
2807    Handle request of IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceUniqueIdProperty.
2808 
2809 Arguments:
2810 
2811     DeviceExtension - device context
2812     Request - request to be handled
2813     RequestParameters - request parameter
2814     DataLength - transferred data length
2815 
2816 Return Value:
2817 
2818     NTSTATUS
2819 
2820 --*/
2821 {
2822     NTSTATUS                    status = STATUS_SUCCESS;
2823     PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
2824     PSTORAGE_PROPERTY_QUERY     inputBuffer = NULL;
2825     PSTORAGE_DESCRIPTOR_HEADER  descHeader = NULL;
2826     size_t                      outLength = 0;
2827     WDF_REQUEST_PARAMETERS      requestParameters;
2828 
2829     // Get the Request parameters
2830     WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
2831     WdfRequestGetParameters(Request, &requestParameters);
2832 
2833     status = WdfRequestRetrieveInputBuffer(Request,
2834                                            requestParameters.Parameters.DeviceIoControl.InputBufferLength,
2835                                            &inputBuffer,
2836                                            NULL);
2837 
2838     if (NT_SUCCESS(status))
2839     {
2840         BOOLEAN overflow = FALSE;
2841         BOOLEAN infoFound = FALSE;
2842 
2843         // Must run at less then dispatch.
2844         if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
2845         {
2846             NT_ASSERT(FALSE);
2847             outLength = 0;
2848             status = STATUS_INVALID_LEVEL;
2849         }
2850         else if (inputBuffer->QueryType == PropertyExistsQuery)
2851         {
2852             outLength = 0;
2853             status = STATUS_SUCCESS;
2854         }
2855         else if (inputBuffer->QueryType != PropertyStandardQuery)
2856         {
2857             outLength = 0;
2858             status = STATUS_NOT_SUPPORTED;
2859         }
2860         else
2861         {
2862             // Check AdditionalParameters validity.
2863             if (inputBuffer->AdditionalParameters[0] == DUID_INCLUDE_SOFTWARE_IDS)
2864             {
2865                 // Do nothing
2866             }
2867             else if (inputBuffer->AdditionalParameters[0] == DUID_HARDWARE_IDS_ONLY)
2868             {
2869                 // Do nothing
2870             }
2871             else
2872             {
2873                 outLength = 0;
2874                 status = STATUS_INVALID_PARAMETER;
2875             }
2876 
2877             if (NT_SUCCESS(status) &&
2878                 (outLength < sizeof(STORAGE_DESCRIPTOR_HEADER)))
2879             {
2880                 outLength = 0;
2881                 status = STATUS_INFO_LENGTH_MISMATCH;
2882             }
2883         }
2884 
2885         // From this point forward the status depends on the overflow
2886         // and infoFound flags.
2887         if (NT_SUCCESS(status))
2888         {
2889             outLength = requestParameters.Parameters.DeviceIoControl.OutputBufferLength;
2890             status = WdfRequestRetrieveOutputBuffer(Request,
2891                                                     requestParameters.Parameters.DeviceIoControl.OutputBufferLength,
2892                                                     &descHeader,
2893                                                     NULL);
2894         }
2895 
2896         if (NT_SUCCESS(status))
2897         {
2898             RtlZeroMemory(descHeader, outLength);
2899 
2900             descHeader->Version = DUID_VERSION_1;
2901             descHeader->Size = sizeof(STORAGE_DEVICE_UNIQUE_IDENTIFIER);
2902 
2903             // Try to build device unique id from StorageDeviceIdProperty.
2904             status = RequestDuidGetDeviceIdProperty(deviceExtension,
2905                                                     Request,
2906                                                     requestParameters,
2907                                                     &outLength);
2908 
2909             if (status == STATUS_BUFFER_OVERFLOW)
2910             {
2911                 overflow = TRUE;
2912             }
2913 
2914             if (NT_SUCCESS(status))
2915             {
2916                 infoFound = TRUE;
2917             }
2918 
2919             // Try to build device unique id from StorageDeviceProperty.
2920             status = RequestDuidGetDeviceProperty(deviceExtension,
2921                                                   Request,
2922                                                   requestParameters,
2923                                                   &outLength);
2924 
2925             if (status == STATUS_BUFFER_OVERFLOW)
2926             {
2927                 overflow = TRUE;
2928             }
2929 
2930             if (NT_SUCCESS(status))
2931             {
2932                 infoFound = TRUE;
2933             }
2934 
2935             // Return overflow, success, or a generic error.
2936             if (overflow)
2937             {
2938                 // If output buffer is STORAGE_DESCRIPTOR_HEADER, then return
2939                 // success to the user.  Otherwise, send an error so the user
2940                 // knows a larger buffer is required.
2941                 if (outLength == sizeof(STORAGE_DESCRIPTOR_HEADER))
2942                 {
2943                     status = STATUS_SUCCESS;
2944                 }
2945                 else
2946                 {
2947                     outLength = (ULONG)WdfRequestGetInformation(Request);
2948                     status = STATUS_BUFFER_OVERFLOW;
2949                 }
2950 
2951             }
2952             else if (infoFound)
2953             {
2954                 status = STATUS_SUCCESS;
2955 
2956                 // Exercise the compare routine.  This should always succeed.
2957                 NT_ASSERT(DuidExactMatch == CompareStorageDuids((PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader,
2958                                                              (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader));
2959 
2960             }
2961             else
2962             {
2963                 status = STATUS_NOT_FOUND;
2964             }
2965         }
2966     }
2967 
2968     RequestCompletion(deviceExtension, Request, status, outLength);
2969 
2970     return status;
2971 }
2972 
2973 NTSTATUS
RequestHandleQueryPropertyWriteCache(_In_ WDFDEVICE Device,_In_ WDFREQUEST Request)2974 RequestHandleQueryPropertyWriteCache(
2975     _In_ WDFDEVICE    Device,
2976     _In_ WDFREQUEST   Request
2977     )
2978 /*++
2979 
2980 Routine Description:
2981 
2982    Handle request of IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceWriteCacheProperty.
2983 
2984 Arguments:
2985 
2986     DeviceExtension - device context
2987     Request - request to be handled
2988 
2989 Return Value:
2990 
2991     NTSTATUS
2992 
2993 --*/
2994 {
2995     NTSTATUS                      status = STATUS_SUCCESS;
2996     PCDROM_DEVICE_EXTENSION       deviceExtension = DeviceGetExtension(Device);
2997     PSTORAGE_PROPERTY_QUERY       query = NULL;
2998     PSTORAGE_WRITE_CACHE_PROPERTY writeCache = NULL;
2999     PMODE_PARAMETER_HEADER        modeData = NULL;
3000     PMODE_CACHING_PAGE            pageData = NULL;
3001     size_t                        length = 0;
3002     ULONG                         information = 0;
3003     PSCSI_REQUEST_BLOCK           srb = NULL;
3004     WDF_REQUEST_PARAMETERS        requestParameters;
3005 
3006     // Get the Request parameters
3007     WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
3008     WdfRequestGetParameters(Request, &requestParameters);
3009 
3010     status = WdfRequestRetrieveInputBuffer(Request,
3011                                            requestParameters.Parameters.DeviceIoControl.InputBufferLength,
3012                                            &query,
3013                                            NULL);
3014 
3015     if (NT_SUCCESS(status))
3016     {
3017 
3018         // Must run at less then dispatch.
3019         if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
3020         {
3021             NT_ASSERT(FALSE);
3022             status = STATUS_INVALID_LEVEL;
3023         }
3024         else if (query->QueryType == PropertyExistsQuery)
3025         {
3026             information = 0;
3027             status = STATUS_SUCCESS;
3028         }
3029         else if (query->QueryType != PropertyStandardQuery)
3030         {
3031             status = STATUS_NOT_SUPPORTED;
3032         }
3033     }
3034 
3035     if (NT_SUCCESS(status))
3036     {
3037         length = requestParameters.Parameters.DeviceIoControl.OutputBufferLength;
3038 
3039         if (length < sizeof(STORAGE_DESCRIPTOR_HEADER))
3040         {
3041             status = STATUS_INFO_LENGTH_MISMATCH;
3042         }
3043     }
3044 
3045     if (NT_SUCCESS(status))
3046     {
3047         status = WdfRequestRetrieveOutputBuffer(Request,
3048                                                 requestParameters.Parameters.DeviceIoControl.OutputBufferLength,
3049                                                 &writeCache,
3050                                                 NULL);
3051     }
3052 
3053     if (NT_SUCCESS(status))
3054     {
3055         RtlZeroMemory(writeCache, length);
3056 
3057         // Set version and required size.
3058         writeCache->Version = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
3059         writeCache->Size = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
3060 
3061         if (length < sizeof(STORAGE_WRITE_CACHE_PROPERTY))
3062         {
3063             // caller only wants header information, bail out.
3064             information = sizeof(STORAGE_DESCRIPTOR_HEADER);
3065             status = STATUS_SUCCESS;
3066 
3067             RequestCompletion(deviceExtension, Request, status, information);
3068             return status;
3069         }
3070     }
3071 
3072     if (NT_SUCCESS(status))
3073     {
3074         srb = ExAllocatePoolWithTag(NonPagedPoolNx,
3075                                     sizeof(SCSI_REQUEST_BLOCK) +
3076                                     (sizeof(ULONG_PTR) * 2),
3077                                     CDROM_TAG_SRB);
3078 
3079         if (srb == NULL)
3080         {
3081             status = STATUS_INSUFFICIENT_RESOURCES;
3082         }
3083     }
3084 
3085     if (NT_SUCCESS(status))
3086     {
3087         RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
3088 
3089         // Set known values
3090         writeCache->NVCacheEnabled = FALSE;
3091         writeCache->UserDefinedPowerProtection = TEST_FLAG(deviceExtension->DeviceFlags, DEV_POWER_PROTECTED);
3092 
3093         // Check for flush cache support by sending a sync cache command
3094         // to the device.
3095 
3096         // Set timeout value and mark the request as not being a tagged request.
3097         srb->Length = SCSI_REQUEST_BLOCK_SIZE;
3098         srb->TimeOutValue = TimeOutValueGetCapValue(deviceExtension->TimeOutValue, 4);
3099         srb->QueueTag = SP_UNTAGGED;
3100         srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
3101         srb->SrbFlags = deviceExtension->SrbFlags;
3102 
3103         srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
3104         srb->CdbLength = 10;
3105 
3106         srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
3107 
3108         status = DeviceSendSrbSynchronously(Device,
3109                                             srb,
3110                                             NULL,
3111                                             0,
3112                                             TRUE,   //flush drive cache
3113                                             Request);
3114 
3115         if (NT_SUCCESS(status))
3116         {
3117             writeCache->FlushCacheSupported = TRUE;
3118         }
3119         else
3120         {
3121             // Device does not support sync cache
3122             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
3123                         "RequestHandleQueryPropertyWriteCache: Synchronize cache failed with status 0x%X\n", status));
3124             writeCache->FlushCacheSupported = FALSE;
3125 
3126             // Reset the status if there was any failure
3127             status = STATUS_SUCCESS;
3128         }
3129 
3130         modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
3131                                          MODE_PAGE_DATA_SIZE,
3132                                          CDROM_TAG_MODE_DATA);
3133 
3134         if (modeData == NULL)
3135         {
3136             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
3137                         "RequestHandleQueryPropertyWriteCache: Unable to allocate mode data buffer\n"));
3138             status = STATUS_INSUFFICIENT_RESOURCES;
3139         }
3140     }
3141 
3142     if (NT_SUCCESS(status))
3143     {
3144         RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
3145 
3146         length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
3147                                                      (PCHAR)modeData,
3148                                                      MODE_PAGE_DATA_SIZE,
3149                                                      MODE_PAGE_CACHING,
3150                                                      MODE_SENSE_CURRENT_VALUES);
3151 
3152         if (length < sizeof(MODE_PARAMETER_HEADER))
3153         {
3154             // Retry the request in case of a check condition.
3155             length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
3156                                                          (PCHAR)modeData,
3157                                                          MODE_PAGE_DATA_SIZE,
3158                                                          MODE_PAGE_CACHING,
3159                                                          MODE_SENSE_CURRENT_VALUES);
3160 
3161             if (length < sizeof(MODE_PARAMETER_HEADER))
3162             {
3163                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Mode Sense failed\n"));
3164                 status = STATUS_IO_DEVICE_ERROR;
3165             }
3166         }
3167     }
3168 
3169     if (NT_SUCCESS(status))
3170     {
3171         // If the length is greater than length indicated by the mode data reset
3172         // the data to the mode data.
3173         if (length > (ULONG) (modeData->ModeDataLength + 1))
3174         {
3175             length = modeData->ModeDataLength + 1;
3176         }
3177 
3178         // Look for caching page in the returned mode page data.
3179         pageData = ModeSenseFindSpecificPage((PCHAR)modeData,
3180                                              length,
3181                                              MODE_PAGE_CACHING,
3182                                              TRUE);
3183 
3184         // Check if valid caching page exists.
3185         if (pageData == NULL)
3186         {
3187             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to find caching mode page.\n"));
3188 
3189             // Set write cache value as unknown.
3190             writeCache->WriteCacheEnabled = WriteCacheEnableUnknown;
3191             writeCache->WriteCacheType = WriteCacheTypeUnknown;
3192         }
3193         else
3194         {
3195             writeCache->WriteCacheEnabled = pageData->WriteCacheEnable
3196                                             ? WriteCacheEnabled
3197                                             : WriteCacheDisabled;
3198 
3199             writeCache->WriteCacheType = pageData->WriteCacheEnable
3200                                          ? WriteCacheTypeWriteBack
3201                                          : WriteCacheTypeUnknown;
3202         }
3203 
3204         // Check write through support.
3205         if (modeData->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED)
3206         {
3207             writeCache->WriteThroughSupported = WriteThroughSupported;
3208         }
3209         else
3210         {
3211             writeCache->WriteThroughSupported = WriteThroughNotSupported;
3212         }
3213 
3214         // Get the changeable caching mode page and check write cache is changeable.
3215         RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
3216 
3217         length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
3218                                                      (PCHAR) modeData,
3219                                                      MODE_PAGE_DATA_SIZE,
3220                                                      MODE_PAGE_CACHING,
3221                                                      MODE_SENSE_CHANGEABLE_VALUES);
3222 
3223         if (length < sizeof(MODE_PARAMETER_HEADER))
3224         {
3225             // Retry the request in case of a check condition.
3226             length = DeviceRetrieveModeSenseUsingScratch(deviceExtension,
3227                                                          (PCHAR) modeData,
3228                                                          MODE_PAGE_DATA_SIZE,
3229                                                          MODE_PAGE_CACHING,
3230                                                          MODE_SENSE_CHANGEABLE_VALUES);
3231 
3232             if (length < sizeof(MODE_PARAMETER_HEADER))
3233             {
3234                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Mode Sense failed\n"));
3235 
3236                 // If the device fails to return changeable pages, then
3237                 // set the write cache changeable value to unknown.
3238                 writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
3239                 information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
3240             }
3241         }
3242     }
3243 
3244     if (NT_SUCCESS(status))
3245     {
3246         // If the length is greater than length indicated by the mode data reset
3247         // the data to the mode data.
3248         if (length > (ULONG) (modeData->ModeDataLength + 1))
3249         {
3250             length = modeData->ModeDataLength + 1;
3251         }
3252 
3253         // Look for caching page in the returned mode page data.
3254         pageData = ModeSenseFindSpecificPage((PCHAR)modeData,
3255                                              length,
3256                                              MODE_PAGE_CACHING,
3257                                              TRUE);
3258         // Check if valid caching page exists.
3259         if (pageData == NULL)
3260         {
3261             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to find caching mode page.\n"));
3262 
3263             // Set write cache changeable value to unknown.
3264             writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
3265         }
3266         else
3267         {
3268             writeCache->WriteCacheChangeable = pageData->WriteCacheEnable
3269                                                ? WriteCacheChangeable
3270                                                : WriteCacheNotChangeable;
3271         }
3272 
3273         information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
3274 
3275     }
3276 
3277     FREE_POOL(srb);
3278     FREE_POOL(modeData);
3279 
3280     RequestCompletion(deviceExtension, Request, status, information);
3281 
3282     return status;
3283 }
3284 
3285 NTSTATUS
RequestValidateDvdReadKey(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)3286 RequestValidateDvdReadKey(
3287     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
3288     _In_  WDFREQUEST               Request,
3289     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
3290     _Out_ size_t *                 DataLength
3291     )
3292 /*++
3293 
3294 Routine Description:
3295 
3296    Validate request of IOCTL_DVD_READ_KEY
3297 
3298 Arguments:
3299 
3300     DeviceExtension - device context
3301     Request - request to be handled
3302     RequestParameters - request parameter
3303     DataLength - transferred data length
3304 
3305 Return Value:
3306 
3307     NTSTATUS
3308 
3309 --*/
3310 {
3311     NTSTATUS                status = STATUS_SUCCESS;
3312     PDVD_COPY_PROTECT_KEY   keyParameters = NULL;
3313     ULONG                   keyLength = 0;
3314 
3315     *DataLength = 0;
3316 
3317     status = WdfRequestRetrieveInputBuffer(Request,
3318                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
3319                                            &keyParameters,
3320                                            NULL);
3321 
3322     if (NT_SUCCESS(status))
3323     {
3324         if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY))
3325         {
3326             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
3327                         "DvdDeviceControl: EstablishDriveKey - challenge "
3328                         "key buffer too small\n"));
3329             status = STATUS_INVALID_PARAMETER;
3330         }
3331     }
3332 
3333     if (NT_SUCCESS(status))
3334     {
3335         switch(keyParameters->KeyType)
3336         {
3337 
3338             case DvdChallengeKey:
3339             {
3340                 C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_CHALLENGE_KEY_LENGTH);
3341                 keyLength = DVD_CHALLENGE_KEY_LENGTH;
3342                 break;
3343             }
3344             case DvdBusKey1:
3345             case DvdBusKey2:
3346             {
3347                 C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_BUS_KEY_LENGTH);
3348                 keyLength = DVD_BUS_KEY_LENGTH;
3349                 break;
3350             }
3351             case DvdTitleKey:
3352             {
3353                 C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_TITLE_KEY_LENGTH);
3354                 keyLength = DVD_TITLE_KEY_LENGTH;
3355                 break;
3356             }
3357             case DvdAsf:
3358             {
3359                 C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_ASF_LENGTH);
3360                 keyLength = DVD_ASF_LENGTH;
3361                 break;
3362             }
3363             case DvdDiskKey:
3364             {
3365                 C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_DISK_KEY_LENGTH);
3366                 keyLength = DVD_DISK_KEY_LENGTH;
3367                 break;
3368             }
3369             case DvdGetRpcKey:
3370             {
3371                 C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_RPC_KEY_LENGTH);
3372                 keyLength = DVD_RPC_KEY_LENGTH;
3373                 break;
3374             }
3375             default:
3376             {
3377                 keyLength = sizeof(DVD_COPY_PROTECT_KEY);
3378                 break;
3379             }
3380         }
3381 
3382         if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < keyLength)
3383         {
3384 
3385             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
3386                         "DvdDeviceControl: EstablishDriveKey - output "
3387                         "buffer too small\n"));
3388             status = STATUS_BUFFER_TOO_SMALL;
3389             *DataLength = keyLength;
3390         }
3391         else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength &
3392                  DeviceExtension->AdapterDescriptor->AlignmentMask)
3393         {
3394             status = STATUS_INVALID_PARAMETER;
3395         }
3396         else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
3397         {
3398             // reject the request if it's not a DVD device.
3399             status = STATUS_INVALID_DEVICE_REQUEST;
3400         }
3401     }
3402 
3403     return status;
3404 }
3405 
3406 
3407 NTSTATUS
RequestValidateDvdEndSession(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)3408 RequestValidateDvdEndSession(
3409     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
3410     _In_  WDFREQUEST               Request,
3411     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
3412     _Out_ size_t *                 DataLength
3413     )
3414 /*++
3415 
3416 Routine Description:
3417 
3418    Handle request of IOCTL_DVD_END_SESSION
3419 
3420 Arguments:
3421 
3422     DeviceExtension - device context
3423     Request - request to be handled
3424     RequestParameters - request parameter
3425     DataLength - transferred data length
3426 
3427 Return Value:
3428 
3429     NTSTATUS
3430 
3431 --*/
3432 {
3433     NTSTATUS        status = STATUS_SUCCESS;
3434     PDVD_SESSION_ID sessionId = NULL;
3435 
3436     UNREFERENCED_PARAMETER(DeviceExtension);
3437 
3438     *DataLength = 0;
3439 
3440     status = WdfRequestRetrieveInputBuffer(Request,
3441                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
3442                                            &sessionId,
3443                                            NULL);
3444 
3445     if (NT_SUCCESS(status))
3446     {
3447         if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
3448             sizeof(DVD_SESSION_ID))
3449         {
3450             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
3451                         "DvdDeviceControl: EndSession - input buffer too "
3452                         "small\n"));
3453             status = STATUS_INVALID_PARAMETER;
3454         }
3455     }
3456 
3457     return status;
3458 }
3459 
3460 
3461 NTSTATUS
RequestValidateAacsEndSession(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)3462 RequestValidateAacsEndSession(
3463     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
3464     _In_  WDFREQUEST               Request,
3465     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
3466     _Out_ size_t *                 DataLength
3467     )
3468 /*++
3469 
3470 Routine Description:
3471 
3472    Validate request of IOCTL_AACS_END_SESSION
3473 
3474 Arguments:
3475 
3476     DeviceExtension - device context
3477     Request - request to be handled
3478     RequestParameters - request parameter
3479     DataLength - transferred data length
3480 
3481 Return Value:
3482 
3483     NTSTATUS
3484 
3485 --*/
3486 {
3487     NTSTATUS        status = STATUS_SUCCESS;
3488     PDVD_SESSION_ID sessionId = NULL;
3489     PCDROM_DATA     cdData = &(DeviceExtension->DeviceAdditionalData);
3490 
3491     *DataLength = 0;
3492 
3493     status = WdfRequestRetrieveInputBuffer(Request,
3494                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
3495                                            &sessionId,
3496                                            NULL);
3497 
3498     if (NT_SUCCESS(status))
3499     {
3500         if (!cdData->Mmc.IsAACS)
3501         {
3502             status = STATUS_INVALID_DEVICE_REQUEST;
3503         }
3504         else if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID))
3505         {
3506             status = STATUS_INVALID_PARAMETER;
3507         }
3508     }
3509 
3510     return status;
3511 }
3512 
3513 
3514 NTSTATUS
RequestValidateEnableStreaming(_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)3515 RequestValidateEnableStreaming(
3516     _In_  WDFREQUEST               Request,
3517     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
3518     _Out_ size_t *                 DataLength
3519     )
3520 /*++
3521 
3522 Routine Description:
3523 
3524     Validates an IOCTL_CDROM_ENABLE_STREAMING request
3525 
3526 Arguments:
3527 
3528     Request - request to be handled
3529     RequestParameters - request parameters
3530     DataLength - transferred data length
3531 
3532 Return Value:
3533 
3534     NTSTATUS
3535 
3536 --*/
3537 {
3538     NTSTATUS                    status = STATUS_SUCCESS;
3539 
3540     PCDROM_STREAMING_CONTROL    inputBuffer = NULL;
3541 
3542     *DataLength = 0;
3543 
3544     if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
3545         sizeof(CDROM_STREAMING_CONTROL))
3546     {
3547         status = STATUS_INFO_LENGTH_MISMATCH;
3548     }
3549 
3550     if (NT_SUCCESS(status))
3551     {
3552         // Get the request type using CDROM_STREAMING_CONTROL structure
3553         status = WdfRequestRetrieveInputBuffer(Request,
3554                                                sizeof(CDROM_STREAMING_CONTROL),
3555                                                &inputBuffer,
3556                                                NULL);
3557     }
3558 
3559     if (NT_SUCCESS(status))
3560     {
3561         if (inputBuffer->RequestType != CdromStreamingDisable &&
3562             inputBuffer->RequestType != CdromStreamingEnableForReadOnly &&
3563             inputBuffer->RequestType != CdromStreamingEnableForWriteOnly &&
3564             inputBuffer->RequestType != CdromStreamingEnableForReadWrite)
3565         {
3566             // Unknown request type
3567             status = STATUS_INVALID_PARAMETER;
3568         }
3569     }
3570 
3571     return status;
3572 }
3573 
3574 
3575 NTSTATUS
RequestValidateSendOpcInformation(_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)3576 RequestValidateSendOpcInformation(
3577     _In_  WDFREQUEST               Request,
3578     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
3579     _Out_ size_t *                 DataLength
3580     )
3581 /*++
3582 
3583 Routine Description:
3584 
3585     Validates an IOCTL_CDROM_SEND_OPC_INFORMATION request
3586 
3587 Arguments:
3588 
3589     Request - request to be handled
3590     RequestParameters - request parameters
3591     DataLength - transferred data length
3592 
3593 Return Value:
3594 
3595     NTSTATUS
3596 
3597 --*/
3598 {
3599     NTSTATUS                    status = STATUS_SUCCESS;
3600 
3601     PCDROM_SIMPLE_OPC_INFO      inputBuffer = NULL;
3602 
3603     *DataLength = 0;
3604 
3605     if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
3606         sizeof(CDROM_SIMPLE_OPC_INFO))
3607     {
3608         status = STATUS_INFO_LENGTH_MISMATCH;
3609     }
3610 
3611     if (NT_SUCCESS(status))
3612     {
3613         // Get the request type using CDROM_SIMPLE_OPC_INFO structure
3614         status = WdfRequestRetrieveInputBuffer(Request,
3615                                                sizeof(CDROM_SIMPLE_OPC_INFO),
3616                                                &inputBuffer,
3617                                                NULL);
3618     }
3619 
3620     if (NT_SUCCESS(status))
3621     {
3622         if (inputBuffer->RequestType != SimpleOpcInfo)
3623         {
3624             // Unknown request type
3625             status = STATUS_INVALID_PARAMETER;
3626         }
3627     }
3628 
3629     return status;
3630 }
3631 
3632 
3633 NTSTATUS
RequestValidateGetPerformance(_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)3634 RequestValidateGetPerformance(
3635     _In_  WDFREQUEST               Request,
3636     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
3637     _Out_ size_t *                 DataLength
3638     )
3639 /*++
3640 
3641 Routine Description:
3642 
3643     Validates an IOCTL_CDROM_GET_PERFORMANCE request
3644 
3645 Arguments:
3646 
3647     Request - request to be handled
3648     RequestParameters - request parameter
3649     DataLength - transferred data length
3650 
3651 Return Value:
3652 
3653     NTSTATUS
3654 
3655 --*/
3656 {
3657     NTSTATUS                    status = STATUS_SUCCESS;
3658     PCDROM_WRITE_SPEED_REQUEST  writeSpeedRequest = NULL;
3659     PCDROM_PERFORMANCE_REQUEST  performanceRequest = NULL;
3660 
3661     *DataLength = 0;
3662 
3663     // CDROM_WRITE_SPEED_REQUEST is the smallest performance request that we support.
3664     // We use it to retrieve request type and then check input length more carefully
3665     // on a per request type basis.
3666     if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
3667         sizeof(CDROM_WRITE_SPEED_REQUEST))
3668     {
3669         status = STATUS_INFO_LENGTH_MISMATCH;
3670     }
3671 
3672     if (NT_SUCCESS(status))
3673     {
3674         status = WdfRequestRetrieveInputBuffer(Request,
3675                                                sizeof(CDROM_WRITE_SPEED_REQUEST),
3676                                                (PVOID*)&writeSpeedRequest,
3677                                                NULL);
3678     }
3679 
3680     if (NT_SUCCESS(status))
3681     {
3682         if (writeSpeedRequest->RequestType == CdromPerformanceRequest)
3683         {
3684             // CDROM_PERFORMANCE_REQUEST is bigger than CDROM_WRITE_SPEED_REQUEST,
3685             // so we perform more checks and retrieve more bytes through WDF.
3686             if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
3687                 sizeof(CDROM_PERFORMANCE_REQUEST))
3688             {
3689                 status = STATUS_INFO_LENGTH_MISMATCH;
3690             }
3691             if (NT_SUCCESS(status))
3692             {
3693                 status = WdfRequestRetrieveInputBuffer(Request,
3694                                                        sizeof(CDROM_PERFORMANCE_REQUEST),
3695                                                        &performanceRequest,
3696                                                        NULL);
3697             }
3698 
3699             if (!NT_SUCCESS(status))
3700             {
3701                 // just pass the status code from above
3702             }
3703             // validate all enum-type fields of CDROM_PERFORMANCE_REQUEST
3704             else if (performanceRequest->PerformanceType != CdromReadPerformance &&
3705                      performanceRequest->PerformanceType != CdromWritePerformance)
3706             {
3707                 status = STATUS_INVALID_PARAMETER;
3708             }
3709             else if (performanceRequest->Exceptions != CdromNominalPerformance &&
3710                      performanceRequest->Exceptions != CdromEntirePerformanceList &&
3711                      performanceRequest->Exceptions != CdromPerformanceExceptionsOnly)
3712             {
3713                 status = STATUS_INVALID_PARAMETER;
3714             }
3715             else if (performanceRequest->Tolerance != Cdrom10Nominal20Exceptions)
3716             {
3717                 status = STATUS_INVALID_PARAMETER;
3718             }
3719         }
3720         else if (writeSpeedRequest->RequestType == CdromWriteSpeedRequest)
3721         {
3722             // No additional checks here: all remaining fields are ignored
3723             // if RequestType == CdromWriteSpeedRequest.
3724         }
3725         else
3726         {
3727             status = STATUS_INVALID_PARAMETER;
3728         }
3729     }
3730 
3731     // finally, check output buffer length
3732     if (NT_SUCCESS(status))
3733     {
3734         if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
3735                  sizeof(CDROM_PERFORMANCE_HEADER))
3736         {
3737             status = STATUS_BUFFER_TOO_SMALL;
3738             *DataLength = sizeof(CDROM_PERFORMANCE_HEADER);
3739         }
3740         else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >
3741             ((USHORT)-1))
3742         {
3743             status = STATUS_INVALID_PARAMETER;
3744         }
3745     }
3746 
3747     return status;
3748 }
3749 
3750 
_IRQL_requires_max_(APC_LEVEL)3751 _IRQL_requires_max_(APC_LEVEL)
3752 PCDB
3753 RequestGetScsiPassThroughCdb(
3754     _In_ PIRP Irp
3755     )
3756 /*++
3757 
3758 Routine Description:
3759 
3760     Get the CDB structure from the SCSI pass through
3761 
3762 Arguments:
3763 
3764     Irp - request to be handled
3765 
3766 Return Value:
3767 
3768     PCDB
3769 
3770 --*/
3771 {
3772     PCDB                cdb = NULL;
3773     PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
3774     ULONG               inputBufferLength = 0;
3775     PVOID               inputBuffer = NULL;
3776     BOOLEAN             legacyPassThrough = FALSE;
3777 
3778     PAGED_CODE();
3779 
3780     if (((currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) ||
3781          (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ||
3782          (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
3783          (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX)) &&
3784         (Irp->AssociatedIrp.SystemBuffer != NULL))
3785     {
3786         inputBufferLength = currentIrpStack->Parameters.DeviceIoControl.InputBufferLength;
3787         inputBuffer = Irp->AssociatedIrp.SystemBuffer;
3788         legacyPassThrough = TRUE;
3789 
3790         if ((currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
3791             (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX))
3792         {
3793             legacyPassThrough = FALSE;
3794         }
3795 
3796         //
3797         //  If this is a 32 bit application running on 64 bit then thunk the
3798         //  input structures to grab the cdb.
3799         //
3800 
3801 #if BUILD_WOW64_ENABLED && defined(_WIN64)
3802 
3803         if (IoIs32bitProcess(Irp))
3804         {
3805             if (legacyPassThrough)
3806             {
3807                 if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH32))
3808                 {
3809                     cdb = (PCDB)((PSCSI_PASS_THROUGH32)inputBuffer)->Cdb;
3810                 }
3811             }
3812             else
3813             {
3814                 if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH32_EX))
3815                 {
3816                     cdb = (PCDB)((PSCSI_PASS_THROUGH32_EX)inputBuffer)->Cdb;
3817                 }
3818             }
3819 
3820         }
3821         else
3822 
3823 #endif
3824 
3825         {
3826             if (legacyPassThrough)
3827             {
3828                 if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH))
3829                 {
3830                     cdb = (PCDB)((PSCSI_PASS_THROUGH)inputBuffer)->Cdb;
3831                 }
3832             }
3833             else
3834             {
3835                 if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH_EX))
3836                 {
3837                     cdb = (PCDB)((PSCSI_PASS_THROUGH_EX)inputBuffer)->Cdb;
3838                 }
3839             }
3840         }
3841     }
3842 
3843     return cdb;
3844 }
3845 
_IRQL_requires_max_(APC_LEVEL)3846 _IRQL_requires_max_(APC_LEVEL)
3847 NTSTATUS
3848 RequestHandleScsiPassThrough(
3849     _In_ WDFDEVICE    Device,
3850     _In_ WDFREQUEST   Request
3851     )
3852 /*++
3853 
3854 Routine Description:
3855 
3856    Handle request of IOCTL_SCSI_PASS_THROUGH
3857                      IOCTL_SCSI_PASS_THROUGH_DIRECT
3858 
3859    The function sets the MinorFunction field of irpStack,
3860    and pass the request to lower level driver.
3861 
3862 Arguments:
3863 
3864     Device - device object
3865     Request - request to be handled
3866 
3867 Return Value:
3868 
3869     NTSTATUS
3870 
3871 --*/
3872 {
3873     NTSTATUS                    status = STATUS_UNSUCCESSFUL;
3874     PCDROM_DEVICE_EXTENSION     deviceExtension = DeviceGetExtension(Device);
3875     PIRP                        irp = WdfRequestWdmGetIrp(Request);
3876     PZERO_POWER_ODD_INFO        zpoddInfo = deviceExtension->ZeroPowerODDInfo;
3877     PCDB                        cdb = NULL;
3878     BOOLEAN                     isSoftEject = FALSE;
3879 
3880 #if DBG
3881     PCDROM_REQUEST_CONTEXT      requestContext = RequestGetContext(Request);
3882 #endif
3883 
3884 
3885     PAGED_CODE();
3886 
3887 #if DBG
3888     // SPTI is always processed in sync manner.
3889     NT_ASSERT(requestContext->SyncRequired);
3890 #endif
3891 
3892     if ((zpoddInfo != NULL) &&
3893         (zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) &&
3894         (zpoddInfo->Load == 0))                                         // Drawer
3895     {
3896         cdb = RequestGetScsiPassThroughCdb(irp);
3897 
3898         if ((cdb != NULL) &&
3899             (cdb->AsByte[0] == SCSIOP_START_STOP_UNIT) &&
3900             (cdb->START_STOP.LoadEject == 1) &&
3901             (cdb->START_STOP.Start == 0))
3902         {
3903             isSoftEject = TRUE;
3904         }
3905     }
3906 
3907     WdfRequestFormatRequestUsingCurrentType(Request);
3908 
3909     // Special for SPTI, set the MinorFunction.
3910     {
3911         PIO_STACK_LOCATION  nextStack = IoGetNextIrpStackLocation(irp);
3912 
3913         nextStack->MinorFunction = 1;
3914     }
3915 
3916 
3917     status = RequestSend(deviceExtension,
3918                          Request,
3919                          deviceExtension->IoTarget,
3920                          WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
3921                          NULL);
3922 
3923 
3924     if (!NT_SUCCESS(status) &&
3925         (isSoftEject != FALSE))
3926     {
3927         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
3928                    "RequestHandleScsiPassThrough: soft eject detected, device marked as active\n"));
3929 
3930         DeviceMarkActive(deviceExtension, TRUE, FALSE);
3931     }
3932 
3933     RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request));
3934 
3935     return status;
3936 }
3937 
3938 NTSTATUS
RequestHandleMountQueryUniqueId(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)3939 RequestHandleMountQueryUniqueId(
3940     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
3941     _In_  WDFREQUEST               Request,
3942     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
3943     _Out_ size_t *                 DataLength
3944     )
3945 /*++
3946 
3947 Routine Description:
3948 
3949    Handle request of IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
3950 
3951 Arguments:
3952 
3953     DeviceExtension - device context
3954     Request - request to be handled
3955     RequestParameters - request parameter
3956     DataLength - transferred data length
3957 
3958 Return Value:
3959 
3960     NTSTATUS
3961 
3962 --*/
3963 {
3964     NTSTATUS            status = STATUS_SUCCESS;
3965     PMOUNTDEV_UNIQUE_ID uniqueId = NULL;
3966 
3967     *DataLength = 0;
3968 
3969     if (!DeviceExtension->MountedDeviceInterfaceName.Buffer)
3970     {
3971         status = STATUS_INVALID_PARAMETER;
3972     }
3973     else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID))
3974     {
3975         *DataLength = sizeof(MOUNTDEV_UNIQUE_ID);
3976         status = STATUS_BUFFER_TOO_SMALL;
3977     }
3978 
3979     if (NT_SUCCESS(status))
3980     {
3981         status = WdfRequestRetrieveOutputBuffer(Request,
3982                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
3983                                                 &uniqueId,
3984                                                 NULL);
3985     }
3986 
3987     if (NT_SUCCESS(status))
3988     {
3989         RtlZeroMemory(uniqueId, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
3990 
3991         uniqueId->UniqueIdLength = DeviceExtension->MountedDeviceInterfaceName.Length;
3992 
3993         if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
3994             (sizeof(USHORT) + DeviceExtension->MountedDeviceInterfaceName.Length))
3995         {
3996             *DataLength = sizeof(MOUNTDEV_UNIQUE_ID);
3997             status = STATUS_BUFFER_OVERFLOW;
3998         }
3999     }
4000 
4001     if (NT_SUCCESS(status))
4002     {
4003         RtlCopyMemory(uniqueId->UniqueId,
4004                       DeviceExtension->MountedDeviceInterfaceName.Buffer,
4005                       uniqueId->UniqueIdLength);
4006 
4007         *DataLength = sizeof(USHORT) + uniqueId->UniqueIdLength;
4008         status = STATUS_SUCCESS;
4009     }
4010 
4011     return status;
4012 }
4013 
4014 NTSTATUS
RequestHandleMountQueryDeviceName(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)4015 RequestHandleMountQueryDeviceName(
4016     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
4017     _In_  WDFREQUEST               Request,
4018     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
4019     _Out_ size_t *                 DataLength
4020     )
4021 /*++
4022 
4023 Routine Description:
4024 
4025    Handle request of IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
4026 
4027 Arguments:
4028 
4029     DeviceExtension - device context
4030     Request - request to be handled
4031     RequestParameters - request parameter
4032     DataLength - transferred data length
4033 
4034 Return Value:
4035 
4036     NTSTATUS
4037 
4038 --*/
4039 {
4040     NTSTATUS        status = STATUS_SUCCESS;
4041     PMOUNTDEV_NAME  name = NULL;
4042 
4043     *DataLength = 0;
4044 
4045     NT_ASSERT(DeviceExtension->DeviceName.Buffer);
4046 
4047     if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME))
4048     {
4049         status = STATUS_BUFFER_TOO_SMALL;
4050         *DataLength = sizeof(MOUNTDEV_NAME);
4051     }
4052 
4053     if (NT_SUCCESS(status))
4054     {
4055         status = WdfRequestRetrieveOutputBuffer(Request,
4056                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
4057                                                 &name,
4058                                                 NULL);
4059     }
4060 
4061     if (NT_SUCCESS(status))
4062     {
4063         RtlZeroMemory(name, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
4064         name->NameLength = DeviceExtension->DeviceName.Length;
4065 
4066         if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
4067             (sizeof(USHORT) + DeviceExtension->DeviceName.Length))
4068         {
4069             status = STATUS_BUFFER_OVERFLOW;
4070             *DataLength = sizeof(MOUNTDEV_NAME);
4071         }
4072     }
4073 
4074     if (NT_SUCCESS(status))
4075     {
4076         RtlCopyMemory(name->Name,
4077                       DeviceExtension->DeviceName.Buffer,
4078                       name->NameLength);
4079 
4080         status = STATUS_SUCCESS;
4081         *DataLength = sizeof(USHORT) + name->NameLength;
4082     }
4083 
4084     return status;
4085 }
4086 
4087 NTSTATUS
RequestHandleMountQuerySuggestedLinkName(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)4088 RequestHandleMountQuerySuggestedLinkName(
4089     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
4090     _In_  WDFREQUEST               Request,
4091     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
4092     _Out_ size_t *                 DataLength
4093     )
4094 /*++
4095 
4096 Routine Description:
4097 
4098    Handle request of IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
4099 
4100 Arguments:
4101 
4102     DeviceExtension - device context
4103     Request - request to be handled
4104     RequestParameters - request parameter
4105     DataLength - transferred data length
4106 
4107 Return Value:
4108 
4109     NTSTATUS
4110 
4111 --*/
4112 {
4113     NTSTATUS        status = STATUS_SUCCESS;
4114 
4115     PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName = NULL;
4116 
4117     WCHAR                    driveLetterNameBuffer[10] = {0};
4118     RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
4119     PWSTR                    valueName = NULL;
4120     UNICODE_STRING           driveLetterName = {0};
4121 
4122     *DataLength = 0;
4123 
4124     if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <
4125         sizeof(MOUNTDEV_SUGGESTED_LINK_NAME))
4126     {
4127         status = STATUS_BUFFER_TOO_SMALL;
4128         *DataLength = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
4129     }
4130 
4131     if (NT_SUCCESS(status))
4132     {
4133         valueName = ExAllocatePoolWithTag(PagedPool,
4134                                           DeviceExtension->DeviceName.Length + sizeof(WCHAR),
4135                                           CDROM_TAG_STRINGS);
4136         if (valueName == NULL)
4137         {
4138             status = STATUS_INSUFFICIENT_RESOURCES;
4139         }
4140     }
4141 
4142     if (NT_SUCCESS(status))
4143     {
4144         RtlCopyMemory(valueName,
4145                       DeviceExtension->DeviceName.Buffer,
4146                       DeviceExtension->DeviceName.Length);
4147         valueName[DeviceExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
4148 
4149         driveLetterName.Buffer = driveLetterNameBuffer;
4150         driveLetterName.MaximumLength = sizeof(driveLetterNameBuffer);
4151         driveLetterName.Length = 0;
4152 
4153         queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
4154         queryTable[0].Name = valueName;
4155         queryTable[0].EntryContext = &driveLetterName;
4156         queryTable[0].DefaultType = (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
4157 
4158         status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
4159                                         L"\\Registry\\Machine\\System\\DISK", // why hard coded?
4160                                         queryTable, NULL, NULL);
4161     }
4162 
4163     if (NT_SUCCESS(status))
4164     {
4165         if ((driveLetterName.Length == 4) &&
4166             (driveLetterName.Buffer[0] == '%') &&
4167             (driveLetterName.Buffer[1] == ':'))
4168         {
4169             driveLetterName.Buffer[0] = 0xFF;
4170         }
4171         else if ((driveLetterName.Length != 4) ||
4172                  (driveLetterName.Buffer[0] < FirstDriveLetter) ||
4173                  (driveLetterName.Buffer[0] > LastDriveLetter) ||
4174                  (driveLetterName.Buffer[1] != ':'))
4175         {
4176             status = STATUS_NOT_FOUND;
4177         }
4178     }
4179 
4180     if (NT_SUCCESS(status))
4181     {
4182         status = WdfRequestRetrieveOutputBuffer(Request,
4183                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
4184                                                 &suggestedName,
4185                                                 NULL);
4186     }
4187 
4188     if (NT_SUCCESS(status))
4189     {
4190         RtlZeroMemory(suggestedName, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
4191         suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
4192         suggestedName->NameLength = 28;
4193 
4194         *DataLength = FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
4195 
4196         if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < *DataLength)
4197         {
4198             *DataLength = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
4199             status = STATUS_BUFFER_OVERFLOW;
4200         }
4201     }
4202 
4203     if (NT_SUCCESS(status))
4204     {
4205         RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
4206                                L"\\Registry\\Machine\\System\\DISK",
4207                                valueName);
4208 
4209         RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
4210         suggestedName->Name[12] = driveLetterName.Buffer[0];
4211         suggestedName->Name[13] = ':';
4212     }
4213 
4214     FREE_POOL(valueName);
4215 
4216     return status;
4217 }
4218 
_IRQL_requires_max_(APC_LEVEL)4219 _IRQL_requires_max_(APC_LEVEL)
4220 NTSTATUS
4221 RequestHandleReadTOC(
4222     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
4223     _In_  WDFREQUEST               Request,
4224     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
4225     _Out_ size_t *                 DataLength
4226     )
4227 /*++
4228 
4229 Routine Description:
4230 
4231    Handle request of IOCTL_CDROM_READ_TOC
4232                      IOCTL_CDROM_GET_LAST_SESSION
4233 
4234 Arguments:
4235 
4236     DeviceExtension - device context
4237     Request - request to be handled
4238     RequestParameters - request parameter
4239     DataLength - transferred data length
4240 
4241 Return Value:
4242 
4243     NTSTATUS
4244 
4245 --*/
4246 {
4247     NTSTATUS status = STATUS_SUCCESS;
4248     VOID*    outputBuffer = NULL;
4249 
4250     PAGED_CODE ();
4251 
4252     *DataLength = 0;
4253 
4254     if (DeviceIsPlayActive(DeviceExtension->Device))
4255     {
4256         status = STATUS_DEVICE_BUSY;
4257     }
4258 
4259     if (NT_SUCCESS(status))
4260     {
4261         status = WdfRequestRetrieveOutputBuffer(Request,
4262                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
4263                                                 &outputBuffer,
4264                                                 NULL);
4265     }
4266 
4267     // handle the request
4268     if (NT_SUCCESS(status))
4269     {
4270         size_t  transferSize;
4271         CDB     cdb;
4272 
4273         transferSize = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, sizeof(CDROM_TOC));
4274 
4275         RtlZeroMemory(outputBuffer, transferSize);
4276 
4277         ScratchBuffer_BeginUse(DeviceExtension);
4278 
4279         RtlZeroMemory(&cdb, sizeof(CDB));
4280         // Set up the CDB
4281         if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION)
4282         {
4283             // Set format to return first and last session numbers.
4284             cdb.READ_TOC.Format2 = CDROM_READ_TOC_EX_FORMAT_SESSION;
4285         }
4286         else
4287         {
4288             // Use MSF addressing
4289             cdb.READ_TOC.Msf = 1;
4290         }
4291 
4292         cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC;
4293         cdb.READ_TOC.AllocationLength[0] = (UCHAR)(transferSize >> 8);
4294         cdb.READ_TOC.AllocationLength[1] = (UCHAR)(transferSize & 0xFF);
4295 
4296         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, 10);
4297 
4298         if (NT_SUCCESS(status))
4299         {
4300             *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
4301             RtlCopyMemory(outputBuffer,
4302                           DeviceExtension->ScratchContext.ScratchBuffer,
4303                           *DataLength);
4304         }
4305 
4306         ScratchBuffer_EndUse(DeviceExtension);
4307     }
4308 
4309     return status;
4310 }
4311 
_IRQL_requires_max_(APC_LEVEL)4312 _IRQL_requires_max_(APC_LEVEL)
4313 NTSTATUS
4314 RequestHandleReadTocEx(
4315     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
4316     _In_  WDFREQUEST               Request,
4317     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
4318     _Out_ size_t *                 DataLength
4319     )
4320 /*++
4321 
4322 Routine Description:
4323 
4324    Handle request of IOCTL_CDROM_READ_TOC_EX
4325 
4326 Arguments:
4327 
4328     DeviceExtension - device context
4329     Request - request to be handled
4330     RequestParameters - request parameter
4331     DataLength - transferred data length
4332 
4333 Return Value:
4334 
4335     NTSTATUS
4336 
4337 --*/
4338 {
4339     NTSTATUS            status = STATUS_SUCCESS;
4340     PCDROM_READ_TOC_EX  inputBuffer = NULL;
4341     VOID*               outputBuffer = NULL;
4342 
4343     PAGED_CODE ();
4344 
4345     *DataLength = 0;
4346 
4347     if (DeviceIsPlayActive(DeviceExtension->Device))
4348     {
4349         status = STATUS_DEVICE_BUSY;
4350     }
4351 
4352     if (NT_SUCCESS(status))
4353     {
4354         status = WdfRequestRetrieveInputBuffer(Request,
4355                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
4356                                                &inputBuffer,
4357                                                NULL);
4358     }
4359 
4360     if (NT_SUCCESS(status))
4361     {
4362         status = WdfRequestRetrieveOutputBuffer(Request,
4363                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
4364                                                 &outputBuffer,
4365                                                 NULL);
4366     }
4367 
4368     // handle the request
4369     if (NT_SUCCESS(status))
4370     {
4371         size_t  transferSize;
4372         CDB     cdb;
4373 
4374         transferSize = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, MAXUSHORT);
4375         ScratchBuffer_BeginUse(DeviceExtension);
4376 
4377         RtlZeroMemory(&cdb, sizeof(CDB));
4378         // Set up the CDB
4379         cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC;
4380         cdb.READ_TOC.Msf = inputBuffer->Msf;
4381         cdb.READ_TOC.Format2 = inputBuffer->Format;
4382         cdb.READ_TOC.StartingTrack = inputBuffer->SessionTrack;
4383         cdb.READ_TOC.AllocationLength[0] = (UCHAR)(transferSize >> 8);
4384         cdb.READ_TOC.AllocationLength[1] = (UCHAR)(transferSize & 0xFF);
4385 
4386         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, 10);
4387 
4388         if (NT_SUCCESS(status))
4389         {
4390             if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE)
4391             {
4392                 *DataLength = 0;
4393                 status = STATUS_INVALID_DEVICE_REQUEST;
4394             }
4395             else
4396             {
4397                 *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
4398                 RtlCopyMemory(outputBuffer,
4399                               DeviceExtension->ScratchContext.ScratchBuffer,
4400                               *DataLength);
4401             }
4402         }
4403 
4404         ScratchBuffer_EndUse(DeviceExtension);
4405     }
4406 
4407     return status;
4408 }
4409 
4410 
_IRQL_requires_max_(APC_LEVEL)4411 _IRQL_requires_max_(APC_LEVEL)
4412 VOID
4413 GetConfigurationDataConversionTypeAllToTypeOne(
4414     _In_    FEATURE_NUMBER       RequestedFeature,
4415     _In_    PSCSI_REQUEST_BLOCK  Srb,
4416     _Out_   size_t *             DataLength
4417     )
4418 /*++
4419 
4420 Routine Description:
4421 
4422     Some CDROM devices do not handle the GET CONFIGURATION commands with
4423     TYPE ONE request. The command will time out causing a bus reset.
4424     To avoid this problem we set a device flag during start device if the device
4425     fails a TYPE ONE request. If this flag is set the TYPE ONE requests will be
4426     tried as TYPE ALL request and the data will be converted to TYPE ONE format
4427     in this routine.
4428 
4429 Arguments:
4430 
4431     RequestedFeature - device context
4432     Srb - request to be handled
4433     DataLength - transfer data length
4434 
4435 Return Value:
4436 
4437     NTSTATUS
4438 
4439 --*/
4440 {
4441     PFEATURE_HEADER     featureHeader = NULL;
4442     FEATURE_NUMBER      thisFeature;
4443     ULONG               totalLength = 0;
4444     ULONG               featureLength = 0;
4445     ULONG               headerLength = 0;
4446 
4447     PGET_CONFIGURATION_HEADER   header = NULL;
4448 
4449     PAGED_CODE ();
4450 
4451     *DataLength = 0;
4452 
4453     if (Srb->DataTransferLength < RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength))
4454     {
4455         // do not have valid data.
4456         return;
4457     }
4458 
4459     // Calculate the length of valid data available in the
4460     // capabilities buffer from the DataLength field
4461     header = (PGET_CONFIGURATION_HEADER) Srb->DataBuffer;
4462     REVERSE_BYTES(&totalLength, header->DataLength);
4463 
4464     totalLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
4465 
4466     // Make sure the we have enough data in the SRB
4467     totalLength = min(totalLength, Srb->DataTransferLength);
4468 
4469     // If we have received enough data from the device
4470     // check for the given feature.
4471     if (totalLength >= (sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)))
4472     {
4473         // Feature information is present. Verify the feature.
4474         featureHeader = (PFEATURE_HEADER)((PUCHAR)Srb->DataBuffer + sizeof(GET_CONFIGURATION_HEADER));
4475 
4476         thisFeature  = (featureHeader->FeatureCode[0] << 8) | (featureHeader->FeatureCode[1]);
4477 
4478         if (thisFeature == RequestedFeature)
4479         {
4480             // Calculate the feature length
4481             featureLength = sizeof(FEATURE_HEADER) + featureHeader->AdditionalLength;
4482         }
4483     }
4484 
4485     // Calculate the total size
4486     totalLength = sizeof(GET_CONFIGURATION_HEADER) + featureLength;
4487 
4488     headerLength = totalLength -
4489         RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
4490 
4491     REVERSE_BYTES(header->DataLength, &headerLength);
4492 
4493     *DataLength = totalLength;
4494 
4495     return;
4496 }
4497 
_IRQL_requires_max_(APC_LEVEL)4498 _IRQL_requires_max_(APC_LEVEL)
4499 VOID
4500 GetConfigurationDataSynthesize(
4501     _In_reads_bytes_(InputBufferSize)    PVOID           InputBuffer,
4502     _In_                            ULONG           InputBufferSize,
4503     _Out_writes_bytes_(OutputBufferSize)  PVOID           OutputBuffer,
4504     _In_                            size_t          OutputBufferSize,
4505     _In_                            FEATURE_NUMBER  StartingFeature,
4506     _In_                            ULONG           RequestType,
4507     _Out_                           size_t *        DataLength
4508     )
4509 /*++
4510 
4511 Routine Description:
4512 
4513     Get Configuration is a frequently used command, and we don't want it to wake
4514     up the device in case it is in Zero Power state. Before entering Zero Power state,
4515     the complete response of the command is saved in cache, and since the response
4516     is always the same in case there is no media, we can synthesize the response
4517     based on the user request.
4518 
4519 Arguments:
4520 
4521     InputBuffer - buffer containing cached command response
4522     InputBufferSize - size of above buffer
4523     OutputBuffer - buffer to fill in result
4524     OutputBufferSize - size of above buffer
4525     StartingFeature - requested Starting Feature Number
4526     RequestType - requested Request Type
4527     DataLength - transfer data length
4528 
4529 Return Value:
4530 
4531 --*/
4532 {
4533     PFEATURE_HEADER     featureHeader = NULL;
4534     ULONG               validLength = 0;
4535     ULONG               featureLength = 0;
4536     ULONG               headerLength = 0;
4537     PUCHAR              buffer = NULL;
4538     ULONG               bytesRemaining = 0;
4539     FEATURE_NUMBER      featureCode = 0;
4540     BOOLEAN             shouldCopy = FALSE;
4541     size_t              copyLength = 0;
4542     size_t              transferedLength = 0;
4543     size_t              requiredLength = 0;
4544 
4545     PGET_CONFIGURATION_HEADER   header = NULL;
4546 
4547     PAGED_CODE ();
4548 
4549     if (InputBufferSize < sizeof (GET_CONFIGURATION_HEADER))
4550     {
4551         // do not have valid data.
4552         *DataLength = 0;
4553 
4554         return;
4555     }
4556 
4557     // Calculate the length of valid data available in the
4558     // capabilities buffer from the DataLength field
4559     header = (PGET_CONFIGURATION_HEADER) InputBuffer;
4560     REVERSE_BYTES(&validLength, header->DataLength);
4561 
4562     validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
4563 
4564     // Make sure we have enough data
4565     validLength = min(validLength, InputBufferSize);
4566 
4567     // Copy the header first
4568     copyLength = min(OutputBufferSize, sizeof (GET_CONFIGURATION_HEADER));
4569 
4570     RtlMoveMemory(OutputBuffer,
4571                   InputBuffer,
4572                   copyLength);
4573 
4574     transferedLength = copyLength;
4575     requiredLength = sizeof (GET_CONFIGURATION_HEADER);
4576 
4577     if (validLength > sizeof (GET_CONFIGURATION_HEADER))
4578     {
4579         buffer = header->Data;
4580         bytesRemaining = validLength - sizeof (GET_CONFIGURATION_HEADER);
4581 
4582         // Ignore incomplete feature descriptor
4583         while (bytesRemaining >= sizeof (FEATURE_HEADER))
4584         {
4585             featureHeader = (PFEATURE_HEADER) buffer;
4586             shouldCopy = FALSE;
4587 
4588             featureCode = (featureHeader->FeatureCode[0] << 8) | (featureHeader->FeatureCode[1]);
4589             featureLength = sizeof (FEATURE_HEADER) + featureHeader->AdditionalLength;
4590 
4591             if (featureCode >= StartingFeature)
4592             {
4593                 switch (RequestType) {
4594 
4595                 case SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL:
4596 
4597                     shouldCopy = TRUE;
4598                     break;
4599 
4600                 case SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT:
4601 
4602                     if (featureHeader->Current)
4603                     {
4604                         shouldCopy = TRUE;
4605                     }
4606                     break;
4607 
4608                 case SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE:
4609 
4610                     if (featureCode == StartingFeature)
4611                     {
4612                         shouldCopy = TRUE;
4613                     }
4614                     break;
4615 
4616                 default:
4617 
4618                     break;
4619                 }
4620             }
4621 
4622             if (shouldCopy != FALSE)
4623             {
4624                 copyLength = min(featureLength, bytesRemaining);
4625                 copyLength = min(copyLength, OutputBufferSize - transferedLength);
4626 
4627                 RtlMoveMemory((PUCHAR) OutputBuffer + transferedLength,
4628                               buffer,
4629                               copyLength);
4630 
4631                 transferedLength += copyLength;
4632                 requiredLength += featureLength;
4633             }
4634 
4635             buffer += min(featureLength, bytesRemaining);
4636             bytesRemaining -= min(featureLength, bytesRemaining);
4637         }
4638     }
4639 
4640     // Adjust Data Length field in header
4641     if (transferedLength >= RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength))
4642     {
4643         headerLength = (ULONG) requiredLength - RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
4644 
4645         header = (PGET_CONFIGURATION_HEADER) OutputBuffer;
4646         REVERSE_BYTES(header->DataLength, &headerLength);
4647     }
4648 
4649     *DataLength = transferedLength;
4650 
4651     return;
4652 }
4653 
4654 
_IRQL_requires_max_(APC_LEVEL)4655 _IRQL_requires_max_(APC_LEVEL)
4656 NTSTATUS
4657 RequestHandleGetConfiguration(
4658     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
4659     _In_  WDFREQUEST               Request,
4660     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
4661     _Out_ size_t *                 DataLength
4662     )
4663 /*++
4664 
4665 Routine Description:
4666 
4667    Handle request of IOCTL_CDROM_GET_CONFIGURATION
4668 
4669 Arguments:
4670 
4671     DeviceExtension - device context
4672     Request - request to be handled
4673     RequestParameters - request parameter
4674     DataLength - transferred data length
4675 
4676 Return Value:
4677 
4678     NTSTATUS
4679 
4680 --*/
4681 {
4682     NTSTATUS                        status = STATUS_SUCCESS;
4683     PCDROM_DATA                     cdData = &(DeviceExtension->DeviceAdditionalData);
4684     PGET_CONFIGURATION_IOCTL_INPUT  inputBuffer = NULL;
4685     VOID*                           outputBuffer = NULL;
4686     size_t                          transferByteCount = 0;
4687     PZERO_POWER_ODD_INFO            zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
4688     BOOLEAN                         inZeroPowerState = FALSE;
4689 
4690     PAGED_CODE ();
4691 
4692     *DataLength = 0;
4693 
4694     //
4695     if (!cdData->Mmc.IsMmc)
4696     {
4697         status = STATUS_INVALID_DEVICE_REQUEST;
4698     }
4699     else
4700     {
4701         status = WdfRequestRetrieveInputBuffer(Request,
4702                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
4703                                                &inputBuffer,
4704                                                NULL);
4705     }
4706 
4707     if (NT_SUCCESS(status))
4708     {
4709         status = WdfRequestRetrieveOutputBuffer(Request,
4710                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
4711                                                 &outputBuffer,
4712                                                 NULL);
4713     }
4714 
4715     if (NT_SUCCESS(status))
4716     {
4717         // If device is Zero Power state, there should be no media in device, thus we can synthesize the response
4718         // from our cache. Avoid waking up the device in this case.
4719         if ((zpoddInfo != NULL) &&
4720             (zpoddInfo->InZeroPowerState != FALSE))
4721         {
4722             inZeroPowerState = TRUE;
4723         }
4724 
4725         if ((inZeroPowerState == FALSE) ||
4726             (zpoddInfo->GetConfigurationBuffer == NULL))
4727         {
4728             CDB cdb;
4729 
4730             //The maximum number of bytes that a Drive may return
4731             //to describe its Features in one GET CONFIGURATION Command is 65,534
4732             transferByteCount = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, (MAXUSHORT - 1));
4733 
4734             // If this is a TYPE ONE request and if this device can't handle this
4735             // request, then we need to send TYPE ALL request to the device and
4736             // convert the data in the completion routine. If required allocate a big
4737             // buffer to get both configuration and feature header.
4738             if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
4739                 TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
4740             {
4741                 transferByteCount = max(transferByteCount,
4742                                         sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER));
4743             }
4744 
4745             ScratchBuffer_BeginUse(DeviceExtension);
4746 
4747             RtlZeroMemory(&cdb, sizeof(CDB));
4748             // Set up the CDB
4749             cdb.GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION;
4750             cdb.GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(transferByteCount >> 8);
4751             cdb.GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(transferByteCount & 0xff);
4752 
4753             cdb.GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(inputBuffer->Feature >> 8);
4754             cdb.GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(inputBuffer->Feature & 0xff);
4755             cdb.GET_CONFIGURATION.RequestType        = (UCHAR)(inputBuffer->RequestType);
4756 
4757             // If the device does not support TYPE ONE get configuration commands
4758             // then change the request type to TYPE ALL. Convert the returned data to
4759             // TYPE ONE format in the completion routine.
4760             if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
4761                 TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
4762             {
4763 
4764                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
4765                            "DeviceHandleGetConfiguration: Changing TYPE_ONE Get Config to TYPE_ALL\n"));
4766                 cdb.GET_CONFIGURATION.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL;
4767             }
4768 
4769             status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferByteCount, TRUE, &cdb, 12);
4770 
4771             if (NT_SUCCESS(status))
4772             {
4773                 if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
4774                     TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG))
4775                 {
4776 
4777                     if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < sizeof(GET_CONFIGURATION_HEADER))
4778                     {
4779                         // Not enough data to calculate the data length.
4780                         // So assume feature is not present
4781                         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DeviceHandleGetConfiguration: No get config header!\n"));
4782                         *DataLength = 0;
4783                         status = STATUS_INVALID_DEVICE_REQUEST;
4784                     }
4785                     else
4786                     {
4787                         //Some CDROM devices do not handle the GET CONFIGURATION commands with
4788                         //TYPE ONE request. The command will time out causing a bus reset.
4789                         //To avoid this problem we set a device flag during start device if the device
4790                         //fails a TYPE ONE request. If this flag is set the TYPE ONE requests will be
4791                         //tried as TYPE ALL request and the data will be converted to TYPE ONE format
4792                         //in this routine.
4793                         GetConfigurationDataConversionTypeAllToTypeOne(inputBuffer->Feature, DeviceExtension->ScratchContext.ScratchSrb, DataLength);
4794                         *DataLength = min(*DataLength, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength);
4795                     }
4796                 }
4797                 else
4798                 {
4799                     *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
4800                 }
4801 
4802                 // copy data to output buffer
4803                 if (NT_SUCCESS(status))
4804                 {
4805                     RtlMoveMemory(outputBuffer,
4806                                   DeviceExtension->ScratchContext.ScratchBuffer,
4807                                   *DataLength);
4808                 }
4809             }
4810 
4811             ScratchBuffer_EndUse(DeviceExtension);
4812         }
4813         else
4814         {
4815             // We are in Zero Power state, and our cached response is available.
4816             // Synthesize the requested data.
4817             GetConfigurationDataSynthesize(zpoddInfo->GetConfigurationBuffer,
4818                                            zpoddInfo->GetConfigurationBufferSize,
4819                                            outputBuffer,
4820                                            RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
4821                                            inputBuffer->Feature,
4822                                            inputBuffer->RequestType,
4823                                            DataLength
4824                                            );
4825         }
4826     }
4827 
4828     return status;
4829 }
4830 
_IRQL_requires_max_(PASSIVE_LEVEL)4831 _IRQL_requires_max_(PASSIVE_LEVEL)
4832 NTSTATUS
4833 RequestHandleGetDriveGeometry(
4834     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
4835     _In_  WDFREQUEST               Request,
4836     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
4837     _Out_ size_t *                 DataLength
4838     )
4839 /*++
4840 
4841 Routine Description:
4842 
4843    Handle request of IOCTL_DISK_GET_LENGTH_INFO
4844                      IOCTL_DISK_GET_DRIVE_GEOMETRY
4845                      IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
4846                      IOCTL_CDROM_GET_DRIVE_GEOMETRY
4847                      IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX
4848                      IOCTL_STORAGE_READ_CAPACITY
4849 
4850 Arguments:
4851 
4852     DeviceExtension - device context
4853     Request - request to be handled
4854     RequestParameters - request parameter
4855     DataLength - transferred data length
4856 
4857 Return Value:
4858 
4859     NTSTATUS
4860 
4861 --*/
4862 {
4863     NTSTATUS            status = STATUS_SUCCESS;
4864     VOID*               outputBuffer = NULL;
4865 
4866     PAGED_CODE ();
4867 
4868     *DataLength = 0;
4869 
4870     status = WdfRequestRetrieveOutputBuffer(Request,
4871                                             RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
4872                                             &outputBuffer,
4873                                             NULL);
4874 
4875     // Issue ReadCapacity to update device extension
4876     // with information for current media.
4877     if (NT_SUCCESS(status))
4878     {
4879         status = MediaReadCapacity(DeviceExtension->Device);
4880     }
4881 
4882     if (NT_SUCCESS(status))
4883     {
4884         switch(RequestParameters.Parameters.DeviceIoControl.IoControlCode)
4885         {
4886             case IOCTL_DISK_GET_LENGTH_INFO:
4887             {
4888                 PGET_LENGTH_INFORMATION lengthInfo = (PGET_LENGTH_INFORMATION)outputBuffer;
4889 
4890                 lengthInfo->Length = DeviceExtension->PartitionLength;
4891                 *DataLength = sizeof(GET_LENGTH_INFORMATION);
4892                 break;
4893             }
4894             case IOCTL_DISK_GET_DRIVE_GEOMETRY:
4895             case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
4896             {
4897                 PDISK_GEOMETRY geometry = (PDISK_GEOMETRY)outputBuffer;
4898 
4899                 *geometry = DeviceExtension->DiskGeometry;
4900                 *DataLength = sizeof(DISK_GEOMETRY);
4901                 break;
4902             }
4903             case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
4904             case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX:
4905             {
4906                 PDISK_GEOMETRY_EX geometryEx = (PDISK_GEOMETRY_EX)outputBuffer;
4907 
4908                 geometryEx->DiskSize = DeviceExtension->PartitionLength;
4909                 geometryEx->Geometry = DeviceExtension->DiskGeometry;
4910                 *DataLength = FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
4911                 break;
4912             }
4913             case IOCTL_STORAGE_READ_CAPACITY:
4914             {
4915                 PSTORAGE_READ_CAPACITY readCapacity = (PSTORAGE_READ_CAPACITY)outputBuffer;
4916 
4917                 readCapacity->Version = sizeof(STORAGE_READ_CAPACITY);
4918                 readCapacity->Size = sizeof(STORAGE_READ_CAPACITY);
4919 
4920                 readCapacity->BlockLength = DeviceExtension->DiskGeometry.BytesPerSector;
4921                 if (readCapacity->BlockLength > 0)
4922                 {
4923                     readCapacity->NumberOfBlocks.QuadPart = DeviceExtension->PartitionLength.QuadPart/readCapacity->BlockLength;
4924                 }
4925                 else
4926                 {
4927                     readCapacity->NumberOfBlocks.QuadPart = 0;
4928                 }
4929 
4930                 readCapacity->DiskLength = DeviceExtension->PartitionLength;
4931 
4932                 *DataLength = sizeof(STORAGE_READ_CAPACITY);
4933                 break;
4934             }
4935             default:
4936             {
4937                 NT_ASSERT(FALSE);
4938                 break;
4939             }
4940         } // end of switch()
4941     }
4942 
4943     return status;
4944 }
4945 
_IRQL_requires_max_(APC_LEVEL)4946 _IRQL_requires_max_(APC_LEVEL)
4947 NTSTATUS
4948 RequestHandleDiskVerify(
4949     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
4950     _In_  WDFREQUEST               Request,
4951     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
4952     _Out_ size_t *                 DataLength
4953     )
4954 /*++
4955 
4956 Routine Description:
4957 
4958    Handle request of IOCTL_DISK_VERIFY
4959 
4960 Arguments:
4961 
4962     DeviceExtension - device context
4963     Request - request to be handled
4964     RequestParameters - request parameter
4965     DataLength - transferred data length
4966 
4967 Return Value:
4968 
4969     NTSTATUS
4970 
4971 --*/
4972 {
4973     NTSTATUS            status = STATUS_SUCCESS;
4974     PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
4975     PVERIFY_INFORMATION verifyInfo = NULL;
4976 
4977     PAGED_CODE ();
4978 
4979     *DataLength = 0;
4980 
4981     if (!cdData->Mmc.WriteAllowed)
4982     {
4983         status = STATUS_MEDIA_WRITE_PROTECTED;
4984     }
4985 
4986     if (NT_SUCCESS(status))
4987     {
4988         status = WdfRequestRetrieveInputBuffer(Request,
4989                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
4990                                                &verifyInfo,
4991                                                NULL);
4992     }
4993 
4994     // handle the request
4995     if (NT_SUCCESS(status))
4996     {
4997         LARGE_INTEGER byteOffset = {0};
4998 
4999         // Add disk offset to starting sector.
5000         byteOffset.QuadPart = DeviceExtension->StartingOffset.QuadPart +
5001                               verifyInfo->StartingOffset.QuadPart;
5002 
5003         // prevent overflow returning success but only validating small area
5004         if (((DeviceExtension->StartingOffset.QuadPart + verifyInfo->StartingOffset.QuadPart) < DeviceExtension->StartingOffset.QuadPart) ||
5005             ((verifyInfo->Length >> DeviceExtension->SectorShift) > MAXUSHORT) ||
5006             ((byteOffset.QuadPart >> DeviceExtension->SectorShift) > MAXULONG) )
5007         {
5008             status = STATUS_INVALID_PARAMETER;
5009         }
5010         else
5011         {
5012             ULONG   transferSize = 0;
5013             ULONG   timeoutValue = 0;
5014             CDB     cdb;
5015             ULONG   sectorOffset;
5016             USHORT  sectorCount;
5017 
5018             ScratchBuffer_BeginUse(DeviceExtension);
5019 
5020             // Convert byte offset to sector offset.
5021             sectorOffset = (ULONG)(byteOffset.QuadPart >> DeviceExtension->SectorShift);
5022 
5023             // Convert ULONG byte count to USHORT sector count.
5024             sectorCount = (USHORT)(verifyInfo->Length >> DeviceExtension->SectorShift);
5025 
5026             RtlZeroMemory(&cdb, sizeof(CDB));
5027             // Set up the CDB
5028             cdb.CDB10.OperationCode = SCSIOP_VERIFY;
5029 
5030             // Move little endian values into CDB in big endian format.
5031             cdb.CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&sectorOffset)->Byte3;
5032             cdb.CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&sectorOffset)->Byte2;
5033             cdb.CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&sectorOffset)->Byte1;
5034             cdb.CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&sectorOffset)->Byte0;
5035 
5036             cdb.CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&sectorCount)->Byte1;
5037             cdb.CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&sectorCount)->Byte0;
5038 
5039             // The verify command is used by the NT FORMAT utility and
5040             // requests are sent down for 5% of the volume size. The
5041             // request timeout value is calculated based on the number of
5042             // sectors verified.
5043             if (sectorCount != 0)
5044             {
5045                 // sectorCount is a USHORT, so no overflow here...
5046                 timeoutValue = TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, ((sectorCount + 128) / 128));
5047             }
5048 
5049             status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, transferSize, FALSE, &cdb, 10, timeoutValue);
5050 
5051             // nothing to do after the command finishes.
5052             ScratchBuffer_EndUse(DeviceExtension);
5053         }
5054     }
5055 
5056     return status;
5057 }
5058 
5059 
_IRQL_requires_max_(APC_LEVEL)5060 _IRQL_requires_max_(APC_LEVEL)
5061 NTSTATUS
5062 RequestHandleCheckVerify(
5063     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5064     _In_  WDFREQUEST               Request,
5065     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
5066     _Out_ size_t *                 DataLength
5067     )
5068 /*++
5069 
5070 Routine Description:
5071 
5072    Handle request of IOCTL_STORAGE_CHECK_VERIFY2
5073                      IOCTL_STORAGE_CHECK_VERIFY
5074 
5075 Arguments:
5076 
5077     DeviceExtension - device context
5078     Request - request to be handled
5079     RequestParameters - request parameter
5080     DataLength - transferred data length
5081 
5082 Return Value:
5083 
5084     NTSTATUS
5085 
5086 --*/
5087 {
5088     NTSTATUS            status = STATUS_SUCCESS;
5089 
5090     PAGED_CODE ();
5091 
5092     *DataLength = 0;
5093 
5094     if (NT_SUCCESS(status))
5095     {
5096         ULONG   transferSize = 0;
5097         CDB     cdb;
5098 
5099         ScratchBuffer_BeginUse(DeviceExtension);
5100 
5101         RtlZeroMemory(&cdb, sizeof(CDB));
5102         // Set up the CDB
5103         cdb.CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
5104 
5105         status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, transferSize, FALSE, &cdb, 6, CDROM_TEST_UNIT_READY_TIMEOUT);
5106 
5107         if (NT_SUCCESS(status))
5108         {
5109             if((RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_CHECK_VERIFY) &&
5110                (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength))
5111             {
5112                 PULONG outputBuffer = NULL;
5113                 status = WdfRequestRetrieveOutputBuffer(Request,
5114                                                         RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
5115                                                         &outputBuffer,
5116                                                         NULL);
5117 
5118                 if (outputBuffer != NULL)
5119                 {
5120                     *outputBuffer = DeviceExtension->MediaChangeCount;
5121                     *DataLength = sizeof(ULONG);
5122                 }
5123             }
5124             else
5125             {
5126                 *DataLength = 0;
5127             }
5128         }
5129 
5130         // nothing to do after the command finishes.
5131         ScratchBuffer_EndUse(DeviceExtension);
5132     }
5133 
5134     return status;
5135 }
5136 
5137 
_IRQL_requires_max_(APC_LEVEL)5138 _IRQL_requires_max_(APC_LEVEL)
5139 NTSTATUS
5140 RequestHandleFakePartitionInfo(
5141     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5142     _In_  WDFREQUEST               Request,
5143     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
5144     _Out_ size_t *                 DataLength
5145     )
5146 /*++
5147 
5148 Routine Description:
5149 
5150    Handle request of IOCTL_DISK_GET_DRIVE_LAYOUT
5151                      IOCTL_DISK_GET_DRIVE_LAYOUT_EX
5152                      IOCTL_DISK_GET_PARTITION_INFO
5153                      IOCTL_DISK_GET_PARTITION_INFO_EX
5154 
5155 Arguments:
5156 
5157     DeviceExtension - device context
5158     Request - request to be handled
5159     RequestParameters - request parameter
5160     DataLength - transferred data length
5161 
5162 Return Value:
5163 
5164     NTSTATUS
5165 
5166 --*/
5167 {
5168     NTSTATUS    status = STATUS_SUCCESS;
5169     VOID*       outputBuffer = NULL;
5170     ULONG       ioctl = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
5171 
5172     PAGED_CODE ();
5173 
5174     *DataLength = 0;
5175 
5176     if (NT_SUCCESS(status))
5177     {
5178         if ((ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT) &&
5179             (ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT_EX) &&
5180             (ioctl != IOCTL_DISK_GET_PARTITION_INFO) &&
5181             (ioctl != IOCTL_DISK_GET_PARTITION_INFO_EX))
5182         {
5183             status = STATUS_INTERNAL_ERROR;
5184         }
5185     }
5186 
5187     if (NT_SUCCESS(status))
5188     {
5189         status = WdfRequestRetrieveOutputBuffer(Request,
5190                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
5191                                                 &outputBuffer,
5192                                                 NULL);
5193     }
5194 
5195     // handle the request
5196     if (NT_SUCCESS(status))
5197     {
5198         switch (ioctl)
5199         {
5200         case IOCTL_DISK_GET_DRIVE_LAYOUT:
5201             *DataLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]);
5202             RtlZeroMemory(outputBuffer, *DataLength);
5203             break;
5204         case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
5205             *DataLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]);
5206             RtlZeroMemory(outputBuffer, *DataLength);
5207             break;
5208         case IOCTL_DISK_GET_PARTITION_INFO:
5209             *DataLength = sizeof(PARTITION_INFORMATION);
5210             RtlZeroMemory(outputBuffer, *DataLength);
5211             break;
5212         case IOCTL_DISK_GET_PARTITION_INFO_EX:
5213             *DataLength = sizeof(PARTITION_INFORMATION_EX);
5214             RtlZeroMemory(outputBuffer, *DataLength);
5215             break;
5216         default:
5217             NT_ASSERT(!"Invalid ioctl should not have reached this point\n");
5218             break;
5219         }
5220 
5221         // if we are getting the drive layout, then we need to start by
5222         // adding some of the non-partition stuff that says we have
5223         // exactly one partition available.
5224         if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT)
5225         {
5226             PDRIVE_LAYOUT_INFORMATION layout;
5227             layout = (PDRIVE_LAYOUT_INFORMATION)outputBuffer;
5228             layout->PartitionCount = 1;
5229             layout->Signature = 1;
5230             outputBuffer = (PVOID)(layout->PartitionEntry);
5231             ioctl = IOCTL_DISK_GET_PARTITION_INFO;
5232         }
5233         else if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT_EX)
5234         {
5235             PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
5236             layoutEx = (PDRIVE_LAYOUT_INFORMATION_EX)outputBuffer;
5237             layoutEx->PartitionStyle = PARTITION_STYLE_MBR;
5238             layoutEx->PartitionCount = 1;
5239             layoutEx->Mbr.Signature = 1;
5240             outputBuffer = (PVOID)(layoutEx->PartitionEntry);
5241             ioctl = IOCTL_DISK_GET_PARTITION_INFO_EX;
5242         }
5243 
5244         // NOTE: the local var 'ioctl' is now modified to either EX or
5245         // non-EX version. the local var 'systemBuffer' is now pointing
5246         // to the partition information structure.
5247         if (ioctl == IOCTL_DISK_GET_PARTITION_INFO)
5248         {
5249             PPARTITION_INFORMATION partitionInfo;
5250             partitionInfo = (PPARTITION_INFORMATION)outputBuffer;
5251             partitionInfo->RewritePartition = FALSE;
5252             partitionInfo->RecognizedPartition = TRUE;
5253             partitionInfo->PartitionType = PARTITION_FAT32;
5254             partitionInfo->BootIndicator = FALSE;
5255             partitionInfo->HiddenSectors = 0;
5256             partitionInfo->StartingOffset.QuadPart = 0;
5257             partitionInfo->PartitionLength = DeviceExtension->PartitionLength;
5258             partitionInfo->PartitionNumber = 0;
5259         }
5260         else
5261         {
5262             PPARTITION_INFORMATION_EX partitionInfo;
5263             partitionInfo = (PPARTITION_INFORMATION_EX)outputBuffer;
5264             partitionInfo->PartitionStyle = PARTITION_STYLE_MBR;
5265             partitionInfo->RewritePartition = FALSE;
5266             partitionInfo->Mbr.RecognizedPartition = TRUE;
5267             partitionInfo->Mbr.PartitionType = PARTITION_FAT32;
5268             partitionInfo->Mbr.BootIndicator = FALSE;
5269             partitionInfo->Mbr.HiddenSectors = 0;
5270             partitionInfo->StartingOffset.QuadPart = 0;
5271             partitionInfo->PartitionLength = DeviceExtension->PartitionLength;
5272             partitionInfo->PartitionNumber = 0;
5273         }
5274     }
5275 
5276     return status;
5277 }
5278 
5279 NTSTATUS
RequestHandleGetDeviceNumber(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)5280 RequestHandleGetDeviceNumber(
5281     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5282     _In_  WDFREQUEST               Request,
5283     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
5284     _Out_ size_t *                 DataLength
5285     )
5286 /*++
5287 
5288 Routine Description:
5289 
5290    Handle request of IOCTL_STORAGE_GET_DEVICE_NUMBER
5291 
5292 Arguments:
5293 
5294     DeviceExtension - device context
5295     Request - request to be handled
5296     RequestParameters - request parameter
5297     DataLength - transferred data length
5298 
5299 Return Value:
5300 
5301     NTSTATUS
5302 
5303 --*/
5304 {
5305     NTSTATUS    status = STATUS_SUCCESS;
5306 
5307     *DataLength = 0;
5308 
5309     if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
5310        sizeof(STORAGE_DEVICE_NUMBER))
5311     {
5312         PSTORAGE_DEVICE_NUMBER deviceNumber = NULL;
5313         status = WdfRequestRetrieveOutputBuffer(Request,
5314                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
5315                                                 &deviceNumber,
5316                                                 NULL);
5317         if (NT_SUCCESS(status))
5318         {
5319             deviceNumber->DeviceType = DeviceExtension->DeviceObject->DeviceType;
5320             deviceNumber->DeviceNumber = DeviceExtension->DeviceNumber;
5321             deviceNumber->PartitionNumber = (ULONG)-1; // legacy reason, return (-1) for this IOCTL.
5322 
5323             status = STATUS_SUCCESS;
5324             *DataLength = sizeof(STORAGE_DEVICE_NUMBER);
5325         }
5326     }
5327     else
5328     {
5329         status = STATUS_BUFFER_TOO_SMALL;
5330         *DataLength = sizeof(STORAGE_DEVICE_NUMBER);
5331     }
5332 
5333     return status;
5334 }
5335 
5336 NTSTATUS
RequestHandleGetHotPlugInfo(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)5337 RequestHandleGetHotPlugInfo(
5338     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5339     _In_  WDFREQUEST               Request,
5340     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
5341     _Out_ size_t *                 DataLength
5342     )
5343 /*++
5344 
5345 Routine Description:
5346 
5347    Handle request of IOCTL_STORAGE_GET_HOTPLUG_INFO
5348 
5349 Arguments:
5350 
5351     DeviceExtension - device context
5352     Request - request to be handled
5353     RequestParameters - request parameter
5354     DataLength - transferred data length
5355 
5356 Return Value:
5357 
5358     NTSTATUS
5359 
5360 --*/
5361 {
5362     NTSTATUS    status = STATUS_SUCCESS;
5363 
5364     *DataLength = 0;
5365 
5366     if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
5367        sizeof(STORAGE_HOTPLUG_INFO))
5368     {
5369         PSTORAGE_HOTPLUG_INFO info = NULL;
5370         status = WdfRequestRetrieveOutputBuffer(Request,
5371                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
5372                                                 &info,
5373                                                 NULL);
5374         if (NT_SUCCESS(status))
5375         {
5376             *info = DeviceExtension->PrivateFdoData->HotplugInfo;
5377 
5378             status = STATUS_SUCCESS;
5379             *DataLength = sizeof(STORAGE_HOTPLUG_INFO);
5380         }
5381     }
5382     else
5383     {
5384         status = STATUS_BUFFER_TOO_SMALL;
5385         *DataLength = sizeof(STORAGE_HOTPLUG_INFO);
5386     }
5387 
5388     return status;
5389 }
5390 
5391 NTSTATUS
RequestHandleSetHotPlugInfo(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)5392 RequestHandleSetHotPlugInfo(
5393     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5394     _In_  WDFREQUEST               Request,
5395     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
5396     _Out_ size_t *                 DataLength
5397     )
5398 /*++
5399 
5400 Routine Description:
5401 
5402    Handle request of IOCTL_STORAGE_SET_HOTPLUG_INFO
5403 
5404 Arguments:
5405 
5406     DeviceExtension - device context
5407     Request - request to be handled
5408     RequestParameters - request parameter
5409     DataLength - transferred data length
5410 
5411 Return Value:
5412 
5413     NTSTATUS
5414 
5415 --*/
5416 {
5417     NTSTATUS                status = STATUS_SUCCESS;
5418     PSTORAGE_HOTPLUG_INFO   info = NULL;
5419 
5420     *DataLength = 0;
5421 
5422     if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
5423         sizeof(STORAGE_HOTPLUG_INFO))
5424     {
5425         // Indicate unsuccessful status and no data transferred.
5426         status = STATUS_INFO_LENGTH_MISMATCH;
5427     }
5428 
5429     if (NT_SUCCESS(status))
5430     {
5431         status = WdfRequestRetrieveInputBuffer(Request,
5432                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
5433                                                &info,
5434                                                NULL);
5435     }
5436 
5437     if (NT_SUCCESS(status))
5438     {
5439         if (info->Size != DeviceExtension->PrivateFdoData->HotplugInfo.Size)
5440         {
5441             status = STATUS_INVALID_PARAMETER_1;
5442         }
5443 
5444         if (info->MediaRemovable != DeviceExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
5445         {
5446             status = STATUS_INVALID_PARAMETER_2;
5447         }
5448 
5449         if (info->MediaHotplug != DeviceExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
5450         {
5451             status = STATUS_INVALID_PARAMETER_3;
5452         }
5453 
5454         if (info->WriteCacheEnableOverride != DeviceExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
5455         {
5456             status = STATUS_INVALID_PARAMETER_5;
5457         }
5458     }
5459 
5460     if (NT_SUCCESS(status))
5461     {
5462         DeviceExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
5463 
5464         // Store the user-defined override in the registry
5465         DeviceSetParameter(DeviceExtension,
5466                            CLASSP_REG_SUBKEY_NAME,
5467                            CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
5468                            (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
5469     }
5470 
5471     return status;
5472 }
5473 
_IRQL_requires_max_(APC_LEVEL)5474 _IRQL_requires_max_(APC_LEVEL)
5475 NTSTATUS
5476 RequestHandleEventNotification(
5477     _In_      PCDROM_DEVICE_EXTENSION  DeviceExtension,
5478     _In_opt_  WDFREQUEST               Request,
5479     _In_opt_  PWDF_REQUEST_PARAMETERS  RequestParameters,
5480     _Out_     size_t *                 DataLength
5481     )
5482 /*++
5483 
5484 Routine Description:
5485 
5486     This routine handles the process of IOCTL_STORAGE_EVENT_NOTIFICATION
5487 
5488 Arguments:
5489 
5490     DeviceExtension - device context
5491 
5492     Request - request to be handled
5493 
5494     RequestParameters - request parameter
5495 
5496     DataLength - data transferred
5497 
5498 Return Value:
5499     NTSTATUS
5500 
5501 --*/
5502 {
5503     NTSTATUS                     status = STATUS_SUCCESS;
5504     PMEDIA_CHANGE_DETECTION_INFO info = NULL;
5505     LONG                         requestInUse;
5506     PSTORAGE_EVENT_NOTIFICATION  eventBuffer = NULL;
5507 
5508     *DataLength = 0;
5509 
5510     info = DeviceExtension->MediaChangeDetectionInfo;
5511 
5512     // Since AN is ASYNCHRONOUS and can happen at any time,
5513     // make certain not to do anything before properly initialized.
5514     if ((!DeviceExtension->IsInitialized) || (info == NULL))
5515     {
5516         status = STATUS_UNSUCCESSFUL;
5517     }
5518 
5519     if (NT_SUCCESS(status) && (Request != NULL) && (RequestParameters != NULL)) {
5520 
5521         //
5522         // Validate IOCTL parameters
5523         //
5524         if (RequestParameters->Parameters.DeviceIoControl.InputBufferLength <
5525             sizeof(STORAGE_EVENT_NOTIFICATION)) {
5526             status = STATUS_INFO_LENGTH_MISMATCH;
5527         }
5528 
5529         //
5530         // Check for an supported event
5531         //
5532         if (NT_SUCCESS(status)) {
5533             status = WdfRequestRetrieveInputBuffer(Request,
5534                                                    RequestParameters->Parameters.DeviceIoControl.InputBufferLength,
5535                                                    &eventBuffer,
5536                                                    NULL);
5537             if (NT_SUCCESS(status)) {
5538                 if ((eventBuffer->Version != STORAGE_EVENT_NOTIFICATION_VERSION_V1) ||
5539                     (eventBuffer->Size != sizeof(STORAGE_EVENT_NOTIFICATION))) {
5540                     status = STATUS_INVALID_PARAMETER;
5541                 } else if ((eventBuffer->Events &
5542                            (STORAGE_EVENT_MEDIA_STATUS | STORAGE_EVENT_DEVICE_STATUS | STORAGE_EVENT_DEVICE_OPERATION)) == 0) {
5543                     status = STATUS_NOT_SUPPORTED;
5544                 }
5545             }
5546         }
5547 
5548     }
5549 
5550     if (NT_SUCCESS(status))
5551     {
5552         if (info->MediaChangeDetectionDisableCount != 0)
5553         {
5554             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
5555                        "RequestHandleEventNotification: device %p has detection disabled \n",
5556                        DeviceExtension->DeviceObject));
5557             status = STATUS_UNSUCCESSFUL;
5558         }
5559     }
5560 
5561     if (NT_SUCCESS(status))
5562     {
5563         // if the request is not in use, mark it as such.
5564         requestInUse = InterlockedCompareExchange((PLONG)&info->MediaChangeRequestInUse, 1, 0);
5565 
5566         if (requestInUse != 0)
5567         {
5568             status = STATUS_UNSUCCESSFUL;
5569         }
5570     }
5571 
5572     if (NT_SUCCESS(status))
5573     {
5574         // The last MCN finished. ok to issue the new one.
5575         RequestSetupMcnSyncIrp(DeviceExtension);
5576 
5577         // The irp will go into KMDF framework and a request will be created there to represent it.
5578         IoCallDriver(DeviceExtension->DeviceObject, info->MediaChangeSyncIrp);
5579     }
5580 
5581     return status;
5582 }
5583 
5584 
_IRQL_requires_max_(PASSIVE_LEVEL)5585 _IRQL_requires_max_(PASSIVE_LEVEL)
5586 NTSTATUS
5587 RequestHandleEjectionControl(
5588     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5589     _In_  WDFREQUEST               Request,
5590     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
5591     _Out_ size_t *                 DataLength
5592     )
5593 /*++
5594 
5595 Routine Description:
5596 
5597    Handle request of IOCTL_STORAGE_MEDIA_REMOVAL
5598                      IOCTL_STORAGE_EJECTION_CONTROL
5599 
5600 Arguments:
5601 
5602     DeviceExtension - device context
5603     Request - request to be handled
5604     RequestParameters - request parameter
5605     DataLength - transferred data length
5606 
5607 Return Value:
5608 
5609     NTSTATUS
5610 
5611 --*/
5612 {
5613     NTSTATUS                status = STATUS_SUCCESS;
5614     PPREVENT_MEDIA_REMOVAL  mediaRemoval = NULL;
5615 
5616     PAGED_CODE ();
5617 
5618     *DataLength = 0;
5619 
5620     if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
5621        sizeof(PREVENT_MEDIA_REMOVAL))
5622     {
5623         status = STATUS_INFO_LENGTH_MISMATCH;
5624     }
5625     else
5626     {
5627         status = WdfRequestRetrieveInputBuffer(Request,
5628                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
5629                                                &mediaRemoval,
5630                                                NULL);
5631     }
5632 
5633     if (NT_SUCCESS(status))
5634     {
5635         status = PerformEjectionControl(DeviceExtension,
5636                                         Request,
5637                                         ((RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EJECTION_CONTROL)
5638                                           ? SecureMediaLock
5639                                           : SimpleMediaLock),
5640                                         mediaRemoval->PreventMediaRemoval);
5641     }
5642 
5643     return status;
5644 }
5645 
5646 
_IRQL_requires_max_(APC_LEVEL)5647 _IRQL_requires_max_(APC_LEVEL)
5648 NTSTATUS
5649 RequestHandleEnableStreaming(
5650     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5651     _In_  WDFREQUEST               Request,
5652     _Out_ size_t *                 DataLength
5653     )
5654 /*++
5655 
5656 Routine Description:
5657 
5658     Handles an IOCTL_CDROM_ENABLE_STREAMING request
5659 
5660 Arguments:
5661 
5662     DeviceExtension - device context
5663     Request - request to be handled
5664     DataLength - transferred data length
5665 
5666 Notes:
5667 
5668     This IOCTL is serialized because it changes read/write
5669     behavior and we want to make sure that all previous
5670     reads/writes have been completed before we change the
5671     behavior.
5672 
5673 Return Value:
5674 
5675     NTSTATUS
5676 
5677 --*/
5678 {
5679     PCDROM_DATA                 cdData = &(DeviceExtension->DeviceAdditionalData);
5680     PCDROM_PRIVATE_FDO_DATA     fdoData = DeviceExtension->PrivateFdoData;
5681 
5682     WDFFILEOBJECT               fileObject = NULL;
5683     PFILE_OBJECT_CONTEXT        fileObjectContext = NULL;
5684 
5685     NTSTATUS                    status = STATUS_SUCCESS;
5686     PCDROM_STREAMING_CONTROL    inputBuffer = NULL;
5687 
5688     BOOLEAN                     enforceStreamingRead = FALSE;
5689     BOOLEAN                     enforceStreamingWrite = FALSE;
5690     BOOLEAN                     streamingReadSupported, streamingWriteSupported;
5691 
5692     PAGED_CODE ();
5693 
5694     *DataLength = 0;
5695 
5696     status = WdfRequestRetrieveInputBuffer(Request,
5697                                            sizeof(CDROM_STREAMING_CONTROL),
5698                                            &inputBuffer,
5699                                            NULL);
5700 
5701     if (NT_SUCCESS(status))
5702     {
5703         // get file object context
5704         fileObject = WdfRequestGetFileObject(Request);
5705         if (fileObject != NULL) {
5706             fileObjectContext = FileObjectGetContext(fileObject);
5707         }
5708         NT_ASSERT(fileObjectContext != NULL);
5709 
5710         if (fileObjectContext == NULL)
5711         {
5712             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
5713                         "RequestHandleEnableStreaming: cannot find file object context\n"));
5714             status = STATUS_INVALID_HANDLE;
5715         }
5716     }
5717 
5718     if (NT_SUCCESS(status))
5719     {
5720         if (inputBuffer->RequestType == CdromStreamingDisable)
5721         {
5722             enforceStreamingRead = FALSE;
5723             enforceStreamingWrite = FALSE;
5724         }
5725         else if (inputBuffer->RequestType == CdromStreamingEnableForReadOnly)
5726         {
5727             enforceStreamingRead = TRUE;
5728             enforceStreamingWrite = FALSE;
5729         }
5730         else if (inputBuffer->RequestType == CdromStreamingEnableForWriteOnly)
5731         {
5732             enforceStreamingRead = FALSE;
5733             enforceStreamingWrite = TRUE;
5734         }
5735         else if (inputBuffer->RequestType == CdromStreamingEnableForReadWrite)
5736         {
5737             enforceStreamingRead = TRUE;
5738             enforceStreamingWrite = TRUE;
5739         }
5740 
5741         streamingReadSupported = cdData->Mmc.StreamingReadSupported && !TEST_FLAG(fdoData->HackFlags, FDO_HACK_NO_STREAMING);
5742         streamingWriteSupported = cdData->Mmc.StreamingWriteSupported && !TEST_FLAG(fdoData->HackFlags, FDO_HACK_NO_STREAMING);
5743         if ((enforceStreamingRead && !streamingReadSupported) ||
5744             (enforceStreamingWrite && !streamingWriteSupported))
5745         {
5746             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
5747                         "RequestHandleEnableStreaming: requested Streaming mode is not supported\n"));
5748             status = STATUS_INVALID_DEVICE_REQUEST;
5749         }
5750         else
5751         {
5752             fileObjectContext->EnforceStreamingRead = enforceStreamingRead;
5753             fileObjectContext->EnforceStreamingWrite = enforceStreamingWrite;
5754         }
5755     }
5756 
5757     return status;
5758 }
5759 
5760 
_IRQL_requires_max_(APC_LEVEL)5761 _IRQL_requires_max_(APC_LEVEL)
5762 NTSTATUS
5763 RequestHandleSendOpcInformation(
5764     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5765     _In_  WDFREQUEST               Request,
5766     _Out_ size_t *                 DataLength
5767     )
5768 /*++
5769 
5770 Routine Description:
5771 
5772     Handles an IOCTL_CDROM_SEND_OPC_INFORMATION request
5773 
5774 Arguments:
5775 
5776     DeviceExtension - device context
5777     Request - request to be handled
5778     DataLength - transferred data length
5779 
5780 Return Value:
5781 
5782     NTSTATUS
5783 
5784 --*/
5785 {
5786     NTSTATUS                status = STATUS_SUCCESS;
5787     PCDROM_SIMPLE_OPC_INFO  inputBuffer = NULL;
5788 
5789     PAGED_CODE ();
5790 
5791     *DataLength = 0;
5792 
5793     status = WdfRequestRetrieveInputBuffer(Request,
5794                                            sizeof(CDROM_SIMPLE_OPC_INFO),
5795                                            (PVOID*)&inputBuffer,
5796                                            NULL);
5797 
5798     if (NT_SUCCESS(status))
5799     {
5800         CDB                         cdb;
5801 
5802         ScratchBuffer_BeginUse(DeviceExtension);
5803 
5804         RtlZeroMemory(&cdb, sizeof(CDB));
5805 
5806         // we support only the simplest version for now
5807         cdb.SEND_OPC_INFORMATION.OperationCode = SCSIOP_SEND_OPC_INFORMATION;
5808         cdb.SEND_OPC_INFORMATION.DoOpc = 1;
5809         cdb.SEND_OPC_INFORMATION.Exclude0 = (inputBuffer->Exclude0 ? 1 : 0);
5810         cdb.SEND_OPC_INFORMATION.Exclude1 = (inputBuffer->Exclude1 ? 1 : 0);
5811 
5812         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, 0, FALSE, &cdb, sizeof(cdb.SEND_OPC_INFORMATION));
5813 
5814         // nothing to do after the command finishes
5815         ScratchBuffer_EndUse(DeviceExtension);
5816     }
5817 
5818     return status;
5819 }
5820 
5821 
_IRQL_requires_max_(APC_LEVEL)5822 _IRQL_requires_max_(APC_LEVEL)
5823 NTSTATUS
5824 RequestHandleGetPerformance(
5825     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5826     _In_  WDFREQUEST               Request,
5827     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
5828     _Out_ size_t *                 DataLength
5829     )
5830 /*++
5831 
5832 Routine Description:
5833 
5834     Handles an IOCTL_CDROM_GET_PERFORMANCE request
5835 
5836 Arguments:
5837 
5838     DeviceExtension - device context
5839     Request - request to be handled
5840     RequestParameters - request parameter
5841     DataLength - transferred data length
5842 
5843 Return Value:
5844 
5845     NTSTATUS
5846 
5847 --*/
5848 {
5849     NTSTATUS                    status = STATUS_SUCCESS;
5850     PCDROM_PERFORMANCE_REQUEST  inputBuffer = NULL;
5851     PVOID                       outputBuffer = NULL;
5852 
5853     PAGED_CODE ();
5854 
5855     *DataLength = 0;
5856 
5857     // Retrieve pointers to input/output data. The size has been validated earlier
5858     // in RequestValidateGetPerformance, so we do not check it again.
5859     if (NT_SUCCESS(status))
5860     {
5861         status = WdfRequestRetrieveInputBuffer(Request,
5862                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
5863                                                (PVOID*)&inputBuffer,
5864                                                NULL);
5865     }
5866     if (NT_SUCCESS(status))
5867     {
5868         status = WdfRequestRetrieveOutputBuffer(Request,
5869                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
5870                                                 &outputBuffer,
5871                                                 NULL);
5872     }
5873 
5874     if (NT_SUCCESS(status))
5875     {
5876         USHORT                      descriptorSize = 0;
5877         USHORT                      descriptorCount = 0;
5878         size_t                      transferSize = 0;
5879         CDB                         cdb;
5880 
5881         ScratchBuffer_BeginUse(DeviceExtension);
5882 
5883         // Set up the CDB
5884         RtlZeroMemory(&cdb, sizeof(CDB));
5885 
5886         if (inputBuffer->RequestType == CdromPerformanceRequest)
5887         {
5888             cdb.GET_PERFORMANCE.Type = 0;
5889 
5890             // 10b is the only defined tolerance in MMCr6
5891             cdb.GET_PERFORMANCE.Tolerance = 2;
5892 
5893             switch (inputBuffer->Exceptions) {
5894                 case CdromNominalPerformance:
5895                     cdb.GET_PERFORMANCE.Except = 0;
5896                     descriptorSize = sizeof(CDROM_NOMINAL_PERFORMANCE_DESCRIPTOR);
5897                     break;
5898                 case CdromEntirePerformanceList:
5899                     cdb.GET_PERFORMANCE.Except = 1;
5900                     descriptorSize = sizeof(CDROM_NOMINAL_PERFORMANCE_DESCRIPTOR);
5901                     break;
5902                 case CdromPerformanceExceptionsOnly:
5903                     cdb.GET_PERFORMANCE.Except = 2;
5904                     descriptorSize = sizeof(CDROM_EXCEPTION_PERFORMANCE_DESCRIPTOR);
5905                     break;
5906             }
5907 
5908             switch (inputBuffer->PerformanceType) {
5909                 case CdromReadPerformance:
5910                     cdb.GET_PERFORMANCE.Write = 0; break;
5911                 case CdromWritePerformance:
5912                     cdb.GET_PERFORMANCE.Write = 1; break;
5913             }
5914 
5915             REVERSE_BYTES(&cdb.GET_PERFORMANCE.StartingLBA, &inputBuffer->StaringLba);
5916         }
5917         else if (inputBuffer->RequestType == CdromWriteSpeedRequest)
5918         {
5919             cdb.GET_PERFORMANCE.Type = 3;
5920             descriptorSize = sizeof(CDROM_WRITE_SPEED_DESCRIPTOR);
5921         }
5922 
5923         cdb.GET_PERFORMANCE.OperationCode = SCSIOP_GET_PERFORMANCE;
5924 
5925         // calculate how many descriptors can fit into the output buffer
5926         if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >=
5927                 sizeof(CDROM_PERFORMANCE_HEADER) &&
5928             RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <=
5929                 MAXUSHORT &&
5930             descriptorSize > 0)
5931         {
5932             descriptorCount = (USHORT)(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength - sizeof(CDROM_PERFORMANCE_HEADER));
5933             descriptorCount /= descriptorSize;
5934         }
5935         else
5936         {
5937             status = STATUS_INVALID_PARAMETER;
5938         }
5939 
5940         REVERSE_BYTES_SHORT(&cdb.GET_PERFORMANCE.MaximumNumberOfDescriptors, &descriptorCount);
5941 
5942         // Calculate transfer size. We round it up to meet adapter requirements.
5943         // Extra bytes are discarded later, when we copy data to the output buffer.
5944         transferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
5945         transferSize += DeviceExtension->AdapterDescriptor->AlignmentMask;
5946         transferSize &= ~DeviceExtension->AdapterDescriptor->AlignmentMask;
5947 
5948         if (NT_SUCCESS(status))
5949         {
5950             status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, sizeof(cdb.GET_PERFORMANCE), CDROM_GET_PERFORMANCE_TIMEOUT);
5951         }
5952 
5953         if (NT_SUCCESS(status))
5954         {
5955             if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < sizeof(CDROM_PERFORMANCE_HEADER))
5956             {
5957                 *DataLength = 0;
5958                 status = STATUS_INVALID_DEVICE_REQUEST;
5959             }
5960             else
5961             {
5962                 *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
5963                                  DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
5964                 RtlCopyMemory(outputBuffer,
5965                               DeviceExtension->ScratchContext.ScratchBuffer,
5966                               *DataLength);
5967             }
5968         }
5969 
5970         ScratchBuffer_EndUse(DeviceExtension);
5971     }
5972 
5973     return status;
5974 }
5975 
_IRQL_requires_max_(APC_LEVEL)5976 _IRQL_requires_max_(APC_LEVEL)
5977 NTSTATUS
5978 RequestHandleMcnSyncFakeIoctl(
5979     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
5980     _Out_ size_t *                 DataLength
5981     )
5982 /*++
5983 
5984 Routine Description:
5985 
5986     Handles an IOCTL_MCN_SYNC_FAKE_IOCTL request
5987 
5988 Arguments:
5989 
5990     DeviceExtension - device context
5991     DataLength - transferred data length
5992 
5993 Return Value:
5994 
5995     NTSTATUS
5996 
5997 --*/
5998 {
5999     NTSTATUS                        status = STATUS_SUCCESS;
6000     PMEDIA_CHANGE_DETECTION_INFO    info = DeviceExtension->MediaChangeDetectionInfo;
6001     BOOLEAN                         shouldRetry = TRUE;
6002     BOOLEAN                         requestSent = FALSE;
6003 
6004     PAGED_CODE ();
6005 
6006     *DataLength = 0;
6007 
6008     //
6009     // Try to acquire the media change event.  If we can't do it immediately
6010     // then bail out and assume the caller will try again later.
6011     //
6012     while (shouldRetry)
6013     {
6014 
6015         status = RequestSetupMcnRequest(DeviceExtension,
6016                                         info->Gesn.Supported);
6017 
6018         if (!NT_SUCCESS(status))
6019         {
6020             shouldRetry = FALSE;
6021         }
6022 
6023         if (NT_SUCCESS(status))
6024         {
6025             requestSent = RequestSendMcnRequest(DeviceExtension);
6026 
6027             if (requestSent)
6028             {
6029                 shouldRetry = RequestPostWorkMcnRequest(DeviceExtension);
6030             }
6031             else
6032             {
6033                 shouldRetry = FALSE;
6034             }
6035         }
6036     }
6037 
6038     // If there were any media change notifications that were not delivered
6039     // for some reason, make an attempt to do so at this time.
6040     DeviceSendDelayedMediaChangeNotifications(DeviceExtension);
6041 
6042     // Set the status and then complete the original request.
6043     // The timer handler will be able to send the next request.
6044     status = STATUS_SUCCESS;
6045 
6046     return status;
6047 }
6048 
6049 BOOLEAN
RequestIsRealtimeStreaming(_In_ WDFREQUEST Request,_In_ BOOLEAN IsReadRequest)6050 RequestIsRealtimeStreaming(
6051     _In_  WDFREQUEST       Request,
6052     _In_  BOOLEAN          IsReadRequest
6053     )
6054 /*++
6055 
6056 Routine Description:
6057 
6058     Checks whether a given read/write request should
6059     be performed in Real-Time Streaming mode.
6060 
6061 Arguments:
6062 
6063     Request - request to be checked
6064     IsReadRequest - TRUE = read request; FALSE = write request
6065 
6066 Return Value:
6067 
6068     TRUE  - a Real-Time Streaming operation has to be performed
6069     FALSE - a normal (non-Streaming) operation has to be performed
6070 
6071 --*/
6072 {
6073     BOOLEAN     useStreaming = FALSE;
6074 
6075     if (!useStreaming) {
6076         //
6077         // Check if we're required to use Streaming via I/O Stack Location flags
6078         //
6079         UCHAR currentStackLocationFlags = 0;
6080         currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request);
6081 
6082         useStreaming = TEST_FLAG(currentStackLocationFlags, SL_REALTIME_STREAM);
6083     }
6084 
6085     if (!useStreaming) {
6086         //
6087         // Check if we were previously requested to enforce Streaming for
6088         // the file handle through which this request was sent.
6089         //
6090 
6091         WDFFILEOBJECT           fileObject;
6092         PFILE_OBJECT_CONTEXT    fileObjectContext;
6093 
6094         fileObject = WdfRequestGetFileObject(Request);
6095 
6096         if (fileObject != NULL) {
6097             fileObjectContext = FileObjectGetContext(fileObject);
6098             NT_ASSERT(fileObjectContext != NULL);
6099 
6100             if (IsReadRequest && fileObjectContext->EnforceStreamingRead)
6101             {
6102                 useStreaming = TRUE;
6103             }
6104 
6105             if (!IsReadRequest && fileObjectContext->EnforceStreamingWrite)
6106             {
6107                 useStreaming = TRUE;
6108             }
6109         }
6110     }
6111 
6112     return useStreaming;
6113 }
6114 
6115 
6116 NTSTATUS
RequestValidateReadWrite(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters)6117 RequestValidateReadWrite(
6118     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
6119     _In_  WDFREQUEST               Request,
6120     _In_  WDF_REQUEST_PARAMETERS   RequestParameters
6121     )
6122 /*++
6123 
6124 Routine Description:
6125 
6126    Validate Read/Write request
6127 
6128 Arguments:
6129 
6130     DeviceExtension - device context
6131     Request - request to be handled
6132     RequestParameters - request parameter
6133 
6134 Return Value:
6135 
6136     NTSTATUS
6137 
6138 --*/
6139 {
6140     NTSTATUS            status = STATUS_SUCCESS;
6141     PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
6142 
6143     BOOLEAN             isValid = TRUE;
6144     LONGLONG            startingOffset = 0;
6145     size_t              transferByteCount = 0;
6146     PIRP                irp = NULL;
6147     PIO_STACK_LOCATION  currentStack = NULL;
6148 
6149     irp = WdfRequestWdmGetIrp(Request);
6150     currentStack = IoGetCurrentIrpStackLocation(irp);
6151 
6152     if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) &&
6153         (currentStack->MinorFunction != CDROM_VOLUME_VERIFY_CHECKED) &&
6154         !TEST_FLAG(currentStack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
6155     {
6156         //  DO_VERIFY_VOLUME is set for the device object,
6157         //  but this request is not itself a verify request.
6158         //  So fail this request.
6159 
6160         //set the status for volume verification.
6161         status = STATUS_VERIFY_REQUIRED;
6162     }
6163 
6164     if (NT_SUCCESS(status))
6165     {
6166         if (PLAY_ACTIVE(DeviceExtension))
6167         {
6168             status = STATUS_DEVICE_BUSY;
6169         }
6170     }
6171 
6172     if (NT_SUCCESS(status))
6173     {
6174         // If the device is in exclusive mode, check whether the request is from
6175         // the handle that locked the device.
6176         if (EXCLUSIVE_MODE(cdData) && !EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request)))
6177         {
6178             // This request is not from the owner. We can't let the operation go.
6179             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Access Denied! Device in exclusive mode.\n"));
6180 
6181             status = STATUS_ACCESS_DENIED;
6182         }
6183     }
6184 
6185     // Validate the request alignment.
6186     if (NT_SUCCESS(status))
6187     {
6188         if (RequestParameters.Type == WdfRequestTypeRead)
6189         {
6190             startingOffset = RequestParameters.Parameters.Read.DeviceOffset;
6191             transferByteCount = RequestParameters.Parameters.Read.Length;
6192         }
6193         else
6194         {
6195             startingOffset = RequestParameters.Parameters.Write.DeviceOffset;
6196             transferByteCount = RequestParameters.Parameters.Write.Length;
6197         }
6198 
6199         if (!DeviceExtension->DiskGeometry.BytesPerSector)
6200         {
6201             DeviceExtension->DiskGeometry.BytesPerSector = 2048;
6202         }
6203 
6204         if (!DeviceExtension->SectorShift)
6205         {
6206             DeviceExtension->SectorShift = 11;
6207         }
6208 
6209         // Perform some basic validation up front
6210         if (TEST_FLAG(startingOffset, DeviceExtension->DiskGeometry.BytesPerSector - 1) ||
6211             TEST_FLAG(transferByteCount,       DeviceExtension->DiskGeometry.BytesPerSector - 1))
6212         {
6213             status = STATUS_INVALID_DEVICE_REQUEST;
6214         }
6215     }
6216 
6217     // validate the request against the current mmc schema
6218     if (NT_SUCCESS(status))
6219     {
6220         FEATURE_NUMBER schema = cdData->Mmc.ValidationSchema;
6221 
6222         // We validate read requests according to the RandomWritable schema, except in the
6223         // case of IncrementalStreamingWritable, wherein  the drive is responsible for all
6224         // of the verification
6225         if (RequestParameters.Type == WdfRequestTypeRead)
6226         {
6227             if (!cdData->Mmc.WriteAllowed)
6228             {
6229                 // standard legacy validation of read irps
6230                 // if writing is not allowed on the media
6231                 schema =  FeatureRandomWritable;
6232             }
6233             else if (schema != FeatureIncrementalStreamingWritable)
6234             {
6235                 // standard legacy validation of read irps
6236                 // if not using streaming writes on writable media
6237                 schema =  FeatureRandomWritable;
6238             }
6239         }
6240 
6241         // Fail write requests to read-only media
6242         if ((RequestParameters.Type == WdfRequestTypeWrite) &&
6243             !(cdData->Mmc.WriteAllowed))
6244         {
6245             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Write request to read-only media\n"));
6246             isValid = FALSE;
6247         }
6248 
6249         if (isValid)
6250         {
6251             switch (schema)
6252             {
6253                 case FeatureDefectManagement:
6254                 case FeatureRandomWritable:
6255                     // Ensure that the request is within bounds for ROM drives.
6256                     // Writer drives do not need to have bounds as outbounds request should not damage the drive.
6257                     if(!cdData->Mmc.IsWriter)
6258                     {
6259                         if ((startingOffset >= DeviceExtension->PartitionLength.QuadPart) ||
6260                             startingOffset < 0)
6261                         {
6262                             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Request is out of bounds\n"));
6263                             isValid = FALSE;
6264 
6265                         }
6266                         else
6267                         {
6268                             ULONGLONG bytesRemaining = DeviceExtension->PartitionLength.QuadPart - startingOffset;
6269 
6270                             if ((ULONGLONG)transferByteCount > bytesRemaining)
6271                             {
6272                                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,  "RequestValidateReadWrite: Request is out of bounds\n"));
6273                                 isValid = FALSE;
6274                             }
6275                         }
6276                     }
6277                     break;
6278 
6279                 case FeatureRigidRestrictedOverwrite:
6280                     // Ensure that the number of blocks is a multiple of the blocking size
6281                     if (((transferByteCount >> DeviceExtension->SectorShift) % cdData->Mmc.Blocking) != 0)
6282                     {
6283                         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
6284                                    "RequestValidateReadWrite: Number of blocks is not a multiple of the blocking size (%x)\n",
6285                                    cdData->Mmc.Blocking));
6286 
6287                         isValid = FALSE;
6288                     }
6289                     // Fall through
6290                 case FeatureRestrictedOverwrite:
6291                     // Ensure that the request begins on a blocking boundary
6292                     if ((Int64ShrlMod32(startingOffset, DeviceExtension->SectorShift) % cdData->Mmc.Blocking) != 0)
6293                     {
6294                         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
6295                                    "RequestValidateReadWrite: Starting block is not a multiple of the blocking size (%x)\n",
6296                                    cdData->Mmc.Blocking));
6297 
6298                         isValid = FALSE;
6299                     }
6300                     break;
6301 
6302                 case FeatureIncrementalStreamingWritable:
6303                     // Let the drive handle the verification
6304                     break;
6305 
6306                 default:
6307                     // Unknown schema. Fail the request
6308                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
6309                                "RequestValidateReadWrite: Unknown validation schema (%x)\n",
6310                                schema));
6311 
6312                     isValid = FALSE;
6313                     break;
6314             } //end of switch (schema)
6315         } // end of if (isValid)
6316 
6317         if (!isValid)
6318         {
6319             status = STATUS_INVALID_DEVICE_REQUEST;
6320         }
6321     } // end of mmc schema validation
6322 
6323     // validate that the Real-Time Streaming requests meet device capabilties
6324     if (NT_SUCCESS(status))
6325     {
6326         // We do not check for FDO_HACK_NO_STREAMING in DeviceExtension->PrivateFdoData->HackFlags here,
6327         // because we're going to hide device failures related to streaming reads/writes from the sender
6328         // of the request. FDO_HACK_NO_STREAMING is going to be taken into account later during actual
6329         // processing of the request.
6330         if (RequestIsRealtimeStreaming(Request, RequestParameters.Type == WdfRequestTypeRead))
6331         {
6332             if (RequestParameters.Type == WdfRequestTypeRead && !cdData->Mmc.StreamingReadSupported)
6333             {
6334                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
6335                            "RequestValidateReadWrite: Streaming reads are not supported.\n"));
6336 
6337                 status = STATUS_INVALID_DEVICE_REQUEST;
6338             }
6339             if (RequestParameters.Type == WdfRequestTypeWrite && !cdData->Mmc.StreamingWriteSupported)
6340             {
6341                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW,
6342                            "RequestValidateReadWrite: Streaming writes are not supported.\n"));
6343 
6344                 status = STATUS_INVALID_DEVICE_REQUEST;
6345             }
6346         }
6347     }
6348 
6349     return status;
6350 }
6351 
6352 NTSTATUS
RequestHandleReadWrite(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters)6353 RequestHandleReadWrite(
6354     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
6355     _In_  WDFREQUEST               Request,
6356     _In_  WDF_REQUEST_PARAMETERS   RequestParameters
6357     )
6358 /*++
6359 
6360 Routine Description:
6361 
6362     Handle a read/write request
6363 
6364 Arguments:
6365 
6366     DeviceExtension - device context
6367     Request - request to be handled
6368     RequestParameters - request parameter
6369 
6370 Return Value:
6371 
6372     NTSTATUS
6373 
6374 --*/
6375 {
6376     NTSTATUS            status = STATUS_SUCCESS;
6377     PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
6378 
6379     size_t              transferByteCount = 0;
6380     PIRP                irp = NULL;
6381     PIO_STACK_LOCATION  currentStack = NULL;
6382 
6383     PUCHAR              dataBuffer;
6384 
6385 
6386     irp = WdfRequestWdmGetIrp(Request);
6387     currentStack = IoGetCurrentIrpStackLocation(irp);
6388     dataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress);
6389 
6390     if (NT_SUCCESS(status))
6391     {
6392         if (RequestParameters.Type == WdfRequestTypeRead)
6393         {
6394             transferByteCount = RequestParameters.Parameters.Read.Length;
6395         }
6396         else
6397         {
6398             transferByteCount = RequestParameters.Parameters.Write.Length;
6399         }
6400 
6401         if (transferByteCount == 0)
6402         {
6403             //  Several parts of the code turn 0 into 0xffffffff,
6404             //  so don't process a zero-length request any further.
6405             status = STATUS_SUCCESS;
6406             RequestCompletion(DeviceExtension, Request, status, 0);
6407             return status;
6408         }
6409 
6410         // Add partition byte offset to make starting byte relative to
6411         // beginning of disk.
6412         currentStack->Parameters.Read.ByteOffset.QuadPart += (DeviceExtension->StartingOffset.QuadPart);
6413 
6414         //not very necessary as the starting offset for CD/DVD device is always 0.
6415         if (RequestParameters.Type == WdfRequestTypeRead)
6416         {
6417             RequestParameters.Parameters.Read.DeviceOffset = currentStack->Parameters.Read.ByteOffset.QuadPart;
6418         }
6419         else
6420         {
6421             RequestParameters.Parameters.Write.DeviceOffset = currentStack->Parameters.Write.ByteOffset.QuadPart;
6422         }
6423     }
6424 
6425     if (NT_SUCCESS(status))
6426     {
6427         ULONG   entireXferLen = currentStack->Parameters.Read.Length;
6428         ULONG   maxLength = 0;
6429         ULONG   packetsCount = 0;
6430 
6431         PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext;
6432         PCDROM_REQUEST_CONTEXT            requestContext;
6433         PCDROM_REQUEST_CONTEXT            originalRequestContext;
6434 
6435         // get the count of packets we need to send.
6436         if ((((ULONG_PTR)dataBuffer) & (PAGE_SIZE-1)) == 0)
6437         {
6438             maxLength = cdData->MaxPageAlignedTransferBytes;
6439         }
6440         else
6441         {
6442             maxLength = cdData->MaxUnalignedTransferBytes;
6443         }
6444 
6445         packetsCount = entireXferLen / maxLength;
6446 
6447         if (entireXferLen % maxLength != 0)
6448         {
6449             packetsCount++;
6450         }
6451 
6452         originalRequestContext = RequestGetContext(Request);
6453 
6454 
6455         ScratchBuffer_BeginUse(DeviceExtension);
6456 
6457         readWriteContext = &DeviceExtension->ScratchContext.ScratchReadWriteContext;
6458         requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
6459 
6460         readWriteContext->PacketsCount = packetsCount;
6461         readWriteContext->EntireXferLen = entireXferLen;
6462         readWriteContext->MaxLength = maxLength;
6463         readWriteContext->StartingOffset = currentStack->Parameters.Read.ByteOffset;
6464         readWriteContext->DataBuffer = dataBuffer;
6465         readWriteContext->TransferedBytes = 0;
6466         readWriteContext->IsRead = (RequestParameters.Type == WdfRequestTypeRead);
6467 
6468         requestContext->OriginalRequest = Request;
6469         requestContext->DeviceExtension = DeviceExtension;
6470 
6471         //
6472         // Setup the READ/WRITE fields in the original request which is what
6473         // we use to properly synchronize cancellation logic between the
6474         // cancel callback and the timer routine.
6475         //
6476 
6477         originalRequestContext->ReadWriteIsCompleted = FALSE;
6478         originalRequestContext->ReadWriteRetryInitialized = FALSE;
6479         originalRequestContext->DeviceExtension = DeviceExtension;
6480 
6481         status = ScratchBuffer_PerformNextReadWrite(DeviceExtension, TRUE);
6482 
6483         // We do not call ScratchBuffer_EndUse here, because we're not releasing the scratch SRB.
6484         // It will be released in the completion routine.
6485     }
6486 
6487     return status;
6488 }
6489 
_IRQL_requires_max_(PASSIVE_LEVEL)6490 _IRQL_requires_max_(PASSIVE_LEVEL)
6491 NTSTATUS
6492 RequestHandleLoadEjectMedia(
6493     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
6494     _In_  WDFREQUEST               Request,
6495     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
6496     _Out_ size_t *                 DataLength
6497     )
6498 /*++
6499 
6500 Routine Description:
6501 
6502    Handle request of IOCTL_STORAGE_EJECT_MEDIA
6503                      IOCTL_STORAGE_LOAD_MEDIA
6504                      IOCTL_STORAGE_LOAD_MEDIA2
6505 
6506 Arguments:
6507 
6508     DeviceExtension - device context
6509     Request - request to be handled
6510     RequestParameters - request parameter
6511     DataLength - transferred data length
6512 
6513 Return Value:
6514 
6515     NTSTATUS
6516 
6517 --*/
6518 {
6519     NTSTATUS                    status = STATUS_SUCCESS;
6520     PZERO_POWER_ODD_INFO        zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
6521 
6522     PAGED_CODE ();
6523 
6524     *DataLength = 0;
6525 
6526     if (NT_SUCCESS(status))
6527     {
6528         // Synchronize with ejection control and ejection cleanup code as
6529         // well as other eject/load requests.
6530         WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL);
6531 
6532         if(DeviceExtension->ProtectedLockCount != 0)
6533         {
6534             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,  "RequestHandleLoadEjectMedia: call to eject protected locked "
6535                                                                 "device - failure\n"));
6536             status = STATUS_DEVICE_BUSY;
6537         }
6538 
6539         if (NT_SUCCESS(status))
6540         {
6541             SCSI_REQUEST_BLOCK  srb;
6542             PCDB                cdb = (PCDB)srb.Cdb;
6543 
6544             RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
6545 
6546             srb.CdbLength = 6;
6547 
6548             cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
6549             cdb->START_STOP.LoadEject = 1;
6550 
6551             if(RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EJECT_MEDIA)
6552             {
6553                 cdb->START_STOP.Start = 0;
6554 
6555                 // We are sending down a soft eject, and in this case we should take an active ref
6556                 // if the command succeeds.
6557                 if ((zpoddInfo != NULL) &&
6558                     (zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) &&
6559                     (zpoddInfo->Load == 0))                                         // Drawer
6560                 {
6561                     zpoddInfo->MonitorStartStopUnit = TRUE;
6562                 }
6563             }
6564             else
6565             {
6566                 cdb->START_STOP.Start = 1;
6567             }
6568 
6569             status = DeviceSendSrbSynchronously(DeviceExtension->Device,
6570                                                 &srb,
6571                                                 NULL,
6572                                                 0,
6573                                                 FALSE,
6574                                                 Request);
6575 
6576             if (zpoddInfo != NULL)
6577             {
6578                 zpoddInfo->MonitorStartStopUnit = FALSE;
6579             }
6580         }
6581 
6582         WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock);
6583     }
6584 
6585     return status;
6586 }
6587 
6588 
_IRQL_requires_max_(PASSIVE_LEVEL)6589 _IRQL_requires_max_(PASSIVE_LEVEL)
6590 NTSTATUS
6591 RequestHandleReserveRelease(
6592     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
6593     _In_  WDFREQUEST               Request,
6594     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
6595     _Out_ size_t *                 DataLength
6596     )
6597 /*++
6598 
6599 Routine Description:
6600 
6601    Handle request of IOCTL_STORAGE_RESERVE
6602                      IOCTL_STORAGE_RELEASE
6603 
6604 Arguments:
6605 
6606     DeviceExtension - device context
6607     Request - request to be handled
6608     RequestParameters - request parameter
6609     DataLength - transferred data length
6610 
6611 Return Value:
6612 
6613     NTSTATUS
6614 
6615 --*/
6616 {
6617     NTSTATUS            status = STATUS_SUCCESS;
6618     PSCSI_REQUEST_BLOCK srb = NULL;
6619     PCDB                cdb = NULL;
6620     ULONG               ioctlCode = 0;
6621 
6622     PAGED_CODE ();
6623 
6624     *DataLength = 0;
6625 
6626     srb = ExAllocatePoolWithTag(NonPagedPoolNx,
6627                          sizeof(SCSI_REQUEST_BLOCK) +
6628                          (sizeof(ULONG_PTR) * 2),
6629                          CDROM_TAG_SRB);
6630 
6631     if (srb == NULL)
6632     {
6633         status = STATUS_INSUFFICIENT_RESOURCES;
6634     }
6635 
6636     if (NT_SUCCESS(status))
6637     {
6638         ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
6639         cdb = (PCDB)srb->Cdb;
6640         RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
6641 
6642         if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6))
6643         {
6644             srb->CdbLength = 10;
6645             cdb->CDB10.OperationCode = (ioctlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
6646         }
6647         else
6648         {
6649             srb->CdbLength = 6;
6650             cdb->CDB6GENERIC.OperationCode = (ioctlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT : SCSIOP_RELEASE_UNIT;
6651         }
6652 
6653         // Set timeout value.
6654         srb->TimeOutValue = DeviceExtension->TimeOutValue;
6655 
6656         // Send reserves as tagged requests.
6657         if (ioctlCode == IOCTL_STORAGE_RESERVE)
6658         {
6659             SET_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
6660             srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
6661         }
6662 
6663         status = DeviceSendSrbSynchronously(DeviceExtension->Device,
6664                                             srb,
6665                                             NULL,
6666                                             0,
6667                                             FALSE,
6668                                             Request);
6669         // no data transfer.
6670         *DataLength = 0;
6671 
6672         FREE_POOL(srb);
6673     }
6674 
6675     return status;
6676 } // end RequestHandleReserveRelease()
6677 
6678 static // __REACTOS__
6679 BOOLEAN
ValidPersistentReserveScope(UCHAR Scope)6680 ValidPersistentReserveScope(
6681     UCHAR Scope)
6682 {
6683     switch (Scope) {
6684     case RESERVATION_SCOPE_LU:
6685     case RESERVATION_SCOPE_ELEMENT:
6686 
6687         return TRUE;
6688 
6689     default:
6690 
6691         return FALSE;
6692     }
6693 }
6694 
6695 static // __REACTOS__
6696 BOOLEAN
ValidPersistentReserveType(UCHAR Type)6697 ValidPersistentReserveType(
6698     UCHAR Type)
6699 {
6700     switch (Type) {
6701     case RESERVATION_TYPE_WRITE_EXCLUSIVE:
6702     case RESERVATION_TYPE_EXCLUSIVE:
6703     case RESERVATION_TYPE_WRITE_EXCLUSIVE_REGISTRANTS:
6704     case RESERVATION_TYPE_EXCLUSIVE_REGISTRANTS:
6705 
6706         return TRUE;
6707 
6708     default:
6709 
6710         return FALSE;
6711     }
6712 }
6713 
6714 NTSTATUS
RequestValidatePersistentReserve(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST Request,_In_ WDF_REQUEST_PARAMETERS RequestParameters,_Out_ size_t * DataLength)6715 RequestValidatePersistentReserve(
6716     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
6717     _In_  WDFREQUEST               Request,
6718     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
6719     _Out_ size_t *                 DataLength
6720     )
6721 /*++
6722 
6723 Routine Description:
6724 
6725    Validate request of IOCTL_STORAGE_PERSISTENT_RESERVE_IN
6726                      IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
6727 
6728 Arguments:
6729 
6730     DeviceExtension - device context
6731     Request - request to be handled
6732     RequestParameters - request parameter
6733     DataLength - transferred data length
6734 
6735 Return Value:
6736 
6737     NTSTATUS
6738 
6739 --*/
6740 {
6741     NTSTATUS            status = STATUS_SUCCESS;
6742     ULONG               ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
6743 
6744     PPERSISTENT_RESERVE_COMMAND reserveCommand = NULL;
6745 
6746     *DataLength = 0;
6747 
6748     status = WdfRequestRetrieveInputBuffer(Request,
6749                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
6750                                            (PVOID*)&reserveCommand,
6751                                            NULL);
6752     if (NT_SUCCESS(status))
6753     {
6754         if ((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(PERSISTENT_RESERVE_COMMAND)) ||
6755             (reserveCommand->Size < sizeof(PERSISTENT_RESERVE_COMMAND)))
6756         {
6757             *DataLength = 0;
6758             status = STATUS_INFO_LENGTH_MISMATCH;
6759         }
6760         else if ((ULONG_PTR)reserveCommand & DeviceExtension->AdapterDescriptor->AlignmentMask)
6761         {
6762             // Check buffer alignment. Only an issue if another kernel mode component
6763             // (not the I/O manager) allocates the buffer.
6764             *DataLength = 0;
6765             status = STATUS_INVALID_USER_BUFFER;
6766         }
6767     }
6768 
6769     if (NT_SUCCESS(status))
6770     {
6771         if (ioctlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN)
6772         {
6773             if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < reserveCommand->PR_IN.AllocationLength)
6774             {
6775                 *DataLength = 0;
6776                 status = STATUS_INVALID_PARAMETER;
6777             }
6778             else
6779             {
6780                 switch (reserveCommand->PR_IN.ServiceAction)
6781                 {
6782                 case RESERVATION_ACTION_READ_KEYS:
6783                     if (reserveCommand->PR_IN.AllocationLength < sizeof(PRI_REGISTRATION_LIST))
6784                     {
6785                         *DataLength = 0;
6786                         status = STATUS_INVALID_PARAMETER;
6787                     }
6788                     break;
6789 
6790                 case RESERVATION_ACTION_READ_RESERVATIONS:
6791                     if (reserveCommand->PR_IN.AllocationLength < sizeof(PRI_RESERVATION_LIST))
6792                     {
6793                         *DataLength = 0;
6794                         status = STATUS_INVALID_PARAMETER;
6795                     }
6796                     break;
6797 
6798                 default:
6799                     *DataLength = 0;
6800                     status = STATUS_INVALID_PARAMETER;
6801                     break;
6802                 }
6803             }
6804         }
6805         else // case of IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
6806         {
6807             // Verify ServiceAction, Scope, and Type
6808             switch (reserveCommand->PR_OUT.ServiceAction)
6809             {
6810             case RESERVATION_ACTION_REGISTER:
6811             case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING:
6812             case RESERVATION_ACTION_CLEAR:
6813                 // Scope and type ignored.
6814                 break;
6815 
6816             case RESERVATION_ACTION_RESERVE:
6817             case RESERVATION_ACTION_RELEASE:
6818             case RESERVATION_ACTION_PREEMPT:
6819             case RESERVATION_ACTION_PREEMPT_ABORT:
6820                 if (!ValidPersistentReserveScope(reserveCommand->PR_OUT.Scope) ||
6821                     !ValidPersistentReserveType(reserveCommand->PR_OUT.Type))
6822                 {
6823                     *DataLength = 0;
6824                     status = STATUS_INVALID_PARAMETER;
6825                 }
6826                 break;
6827 
6828             default:
6829                 *DataLength = 0;
6830                 status = STATUS_INVALID_PARAMETER;
6831                 break;
6832             }
6833 
6834             // Check input buffer for PR Out.
6835             // Caller must include the PR parameter list.
6836             if (NT_SUCCESS(status))
6837             {
6838                 if ((RequestParameters.Parameters.DeviceIoControl.InputBufferLength <
6839                         (sizeof(PERSISTENT_RESERVE_COMMAND) + sizeof(PRO_PARAMETER_LIST))) ||
6840                     (reserveCommand->Size < RequestParameters.Parameters.DeviceIoControl.InputBufferLength))
6841                 {
6842                     *DataLength = 0;
6843                     status = STATUS_INVALID_PARAMETER;
6844                 }
6845             }
6846         } // end of validation for IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
6847     }
6848 
6849     return status;
6850 } // end RequestValidatePersistentReserve()
6851 
6852 
_IRQL_requires_max_(PASSIVE_LEVEL)6853 _IRQL_requires_max_(PASSIVE_LEVEL)
6854 NTSTATUS
6855 RequestHandlePersistentReserve(
6856     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
6857     _In_  WDFREQUEST               Request,
6858     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
6859     _Out_ size_t *                 DataLength
6860     )
6861 /*++
6862 
6863 Routine Description:
6864 
6865    Handle request of IOCTL_STORAGE_PERSISTENT_RESERVE_IN
6866                      IOCTL_STORAGE_PERSISTENT_RESERVE_OUT
6867 
6868 Arguments:
6869 
6870     DeviceExtension - device context
6871     Request - request to be handled
6872     RequestParameters - request parameter
6873     DataLength - transferred data length
6874 
6875 Return Value:
6876 
6877     NTSTATUS
6878 
6879 --*/
6880 {
6881     NTSTATUS            status = STATUS_SUCCESS;
6882     ULONG               ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
6883     PSCSI_REQUEST_BLOCK srb = NULL;
6884     PCDB                cdb = NULL;
6885     BOOLEAN             writeToDevice;
6886 
6887     PPERSISTENT_RESERVE_COMMAND reserveCommand = NULL;
6888 
6889     PAGED_CODE ();
6890 
6891     *DataLength = 0;
6892 
6893     status = WdfRequestRetrieveInputBuffer(Request,
6894                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
6895                                            (PVOID*)&reserveCommand,
6896                                            NULL);
6897     if (NT_SUCCESS(status))
6898     {
6899         srb = ExAllocatePoolWithTag(NonPagedPoolNx,
6900                              sizeof(SCSI_REQUEST_BLOCK) +
6901                              (sizeof(ULONG_PTR) * 2),
6902                              CDROM_TAG_SRB);
6903 
6904         if (srb == NULL)
6905         {
6906             status = STATUS_INSUFFICIENT_RESOURCES;
6907         }
6908         else
6909         {
6910             cdb = (PCDB)srb->Cdb;
6911             RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
6912         }
6913     }
6914 
6915     if (NT_SUCCESS(status))
6916     {
6917         size_t dataBufLen = 0;
6918 
6919         // Fill in the CDB.
6920         if (ioctlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN)
6921         {
6922             cdb->PERSISTENT_RESERVE_IN.OperationCode    = SCSIOP_PERSISTENT_RESERVE_IN;
6923             cdb->PERSISTENT_RESERVE_IN.ServiceAction    = reserveCommand->PR_IN.ServiceAction;
6924 
6925             REVERSE_BYTES_SHORT(&(cdb->PERSISTENT_RESERVE_IN.AllocationLength),
6926                                 &(reserveCommand->PR_IN.AllocationLength));
6927 
6928             dataBufLen = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
6929             writeToDevice = FALSE;
6930         }
6931         else
6932         {
6933             cdb->PERSISTENT_RESERVE_OUT.OperationCode   = SCSIOP_PERSISTENT_RESERVE_OUT;
6934             cdb->PERSISTENT_RESERVE_OUT.ServiceAction   = reserveCommand->PR_OUT.ServiceAction;
6935             cdb->PERSISTENT_RESERVE_OUT.Scope           = reserveCommand->PR_OUT.Scope;
6936             cdb->PERSISTENT_RESERVE_OUT.Type            = reserveCommand->PR_OUT.Type;
6937 
6938             cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = (UCHAR)sizeof(PRO_PARAMETER_LIST);
6939 
6940             // Move the parameter list to the beginning of the data buffer (so it is aligned
6941             // correctly and that the MDL describes it correctly).
6942             RtlMoveMemory(reserveCommand,
6943                           reserveCommand->PR_OUT.ParameterList,
6944                           sizeof(PRO_PARAMETER_LIST));
6945 
6946             dataBufLen = sizeof(PRO_PARAMETER_LIST);
6947             writeToDevice = TRUE;
6948         }
6949 
6950         srb->CdbLength = 10;
6951         srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
6952 
6953         status = DeviceSendSrbSynchronously(DeviceExtension->Device,
6954                                             srb,
6955                                             reserveCommand,
6956                                             (ULONG) dataBufLen,
6957                                             writeToDevice,
6958                                             Request);
6959 
6960         FREE_POOL(srb);
6961     }
6962 
6963     return status;
6964 }
6965 
6966 #if (NTDDI_VERSION >= NTDDI_WIN8)
_IRQL_requires_max_(APC_LEVEL)6967 _IRQL_requires_max_(APC_LEVEL)
6968 NTSTATUS
6969 RequestHandleAreVolumesReady(
6970     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
6971     _In_  WDFREQUEST               Request,
6972     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
6973     _Out_ size_t *                 DataLength
6974     )
6975 /*++
6976 
6977 Routine Description:
6978 
6979    Handle request of IOCTL_DISK_ARE_VOLUMES_READY
6980 
6981 Arguments:
6982 
6983     DeviceExtension - device context
6984     Request - request to be handled
6985     RequestParameters - request parameter
6986     DataLength - transferred data length
6987 
6988 Return Value:
6989 
6990     NTSTATUS
6991 
6992 --*/
6993 {
6994     BOOLEAN                 completeRequest = TRUE;
6995     NTSTATUS                status = STATUS_SUCCESS;
6996 
6997     UNREFERENCED_PARAMETER(RequestParameters);
6998 
6999     *DataLength = 0;
7000 
7001     if (DeviceExtension->IsVolumeOnlinePending == FALSE)
7002     {
7003         status = STATUS_SUCCESS;
7004         goto Cleanup;
7005     }
7006 
7007     //
7008     // Add to the volume ready queue. No worries about request cancellation,
7009     // since KMDF will automatically handle that while request is in queue.
7010     //
7011     status = WdfRequestForwardToIoQueue(Request,
7012                                         DeviceExtension->ManualVolumeReadyQueue
7013                                         );
7014 
7015     if(!NT_SUCCESS(status))
7016     {
7017         goto Cleanup;
7018     }
7019 
7020     status = STATUS_PENDING;
7021     completeRequest = FALSE;
7022 
7023 Cleanup:
7024 
7025     if (completeRequest)
7026     {
7027         RequestCompletion(DeviceExtension, Request, status, 0);
7028     }
7029 
7030     return status;
7031 }
7032 
_IRQL_requires_max_(APC_LEVEL)7033 _IRQL_requires_max_(APC_LEVEL)
7034 NTSTATUS
7035 RequestHandleVolumeOnline(
7036     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7037     _In_  WDFREQUEST               Request,
7038     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
7039     _Out_ size_t *                 DataLength
7040     )
7041 /*++
7042 
7043 Routine Description:
7044 
7045    Handle request of IOCTL_VOLUME_ONLINE / IOCTL_VOLUME_POST_ONLINE
7046 
7047 Arguments:
7048 
7049     DeviceExtension - device context
7050     Request - request to be handled
7051     RequestParameters - request parameter
7052     DataLength - transferred data length
7053 
7054 Return Value:
7055 
7056     NTSTATUS
7057 
7058 --*/
7059 {
7060     NTSTATUS                status = STATUS_SUCCESS;
7061     WDFREQUEST              request;
7062     PIRP                    irp = NULL;
7063     PIO_STACK_LOCATION      nextStack = NULL;
7064 
7065     UNREFERENCED_PARAMETER(RequestParameters);
7066 
7067     *DataLength = 0;
7068 
7069     DeviceExtension->IsVolumeOnlinePending = FALSE;
7070 
7071     // Complete all parked volume ready requests.
7072     for (;;)
7073     {
7074         status = WdfIoQueueRetrieveNextRequest(DeviceExtension->ManualVolumeReadyQueue,
7075                                                &request);
7076 
7077         if (!NT_SUCCESS(status))
7078         {
7079             break;
7080         }
7081 
7082         RequestCompletion(DeviceExtension, request, STATUS_SUCCESS, 0);
7083     }
7084 
7085     // Change the IOCTL code to IOCTL_DISK_VOLUMES_ARE_READY, and forward the request down,
7086     // so that if the underlying port driver will also know that volume is ready.
7087     WdfRequestFormatRequestUsingCurrentType(Request);
7088     irp = WdfRequestWdmGetIrp(Request);
7089     nextStack = IoGetNextIrpStackLocation(irp);
7090 
7091     irp->AssociatedIrp.SystemBuffer = &DeviceExtension->DeviceNumber;
7092     nextStack->Parameters.DeviceIoControl.OutputBufferLength = 0;
7093     nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof (DeviceExtension->DeviceNumber);
7094     nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_DISK_VOLUMES_ARE_READY;
7095 
7096     // Send the request straight down (synchronously).
7097     RequestSend(DeviceExtension,
7098                 Request,
7099                 DeviceExtension->IoTarget,
7100                 WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
7101                 NULL);
7102 
7103     return STATUS_SUCCESS;
7104 }
7105 #endif
7106 
_IRQL_requires_max_(PASSIVE_LEVEL)7107 _IRQL_requires_max_(PASSIVE_LEVEL)
7108 NTSTATUS
7109 DeviceHandleRawRead(
7110     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7111     _In_  WDFREQUEST               Request,
7112     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
7113     _Out_ size_t *                 DataLength
7114     )
7115 /*++
7116 
7117 Routine Description:
7118 
7119    Handle request of IOCTL_CDROM_RAW_READ
7120 
7121 Arguments:
7122 
7123     DeviceExtension - device context
7124     Request - request to be handled
7125     RequestParameters - request parameter
7126     DataLength - transferred data length
7127 
7128 Return Value:
7129 
7130     NTSTATUS
7131 
7132 --*/
7133 {
7134     // Determine whether the drive is currently in raw or cooked mode,
7135     // and which command to use to read the data.
7136 
7137     NTSTATUS        status = STATUS_SUCCESS;
7138     PCDROM_DATA     cdData = &(DeviceExtension->DeviceAdditionalData);
7139     RAW_READ_INFO   rawReadInfo = {0};
7140     PVOID           outputVirtAddr = NULL;
7141     ULONG           startingSector;
7142 
7143     PIRP                irp = WdfRequestWdmGetIrp(Request);
7144     PIO_STACK_LOCATION  currentIrpStack = IoGetCurrentIrpStackLocation(irp);
7145 
7146     VOID*               outputBuffer = NULL;
7147 
7148     PAGED_CODE ();
7149 
7150     *DataLength = 0;
7151 
7152     if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) &&
7153         (currentIrpStack->MinorFunction != CDROM_VOLUME_VERIFY_CHECKED) &&
7154         !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
7155     {
7156         //  DO_VERIFY_VOLUME is set for the device object,
7157         //  but this request is not itself a verify request.
7158         //  So fail this request.
7159         (VOID) MediaReadCapacity(DeviceExtension->Device);
7160 
7161         *DataLength = 0;
7162         //set the status for volume verification.
7163         status = STATUS_VERIFY_REQUIRED;
7164     }
7165 
7166     if (NT_SUCCESS(status))
7167     {
7168         // Since this ioctl is METHOD_OUT_DIRECT, we need to copy away
7169         // the input buffer before interpreting it.  This prevents a
7170         // malicious app from messing with the input buffer while we
7171         // are interpreting it. This is done in dispacth.
7172         //
7173         // Here, we are going to get the input buffer out of saved place.
7174         rawReadInfo = *(PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
7175 
7176         if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength > 0)
7177         {
7178             // Make sure that any user buffer that we pass down to
7179             // the hardware is properly aligned
7180             NT_ASSERT(irp->MdlAddress);
7181             outputVirtAddr = MmGetMdlVirtualAddress(irp->MdlAddress);
7182             if ((ULONG_PTR)outputVirtAddr & DeviceExtension->AdapterDescriptor->AlignmentMask)
7183             {
7184                 NT_ASSERT(!((ULONG_PTR)outputVirtAddr & DeviceExtension->AdapterDescriptor->AlignmentMask));
7185                 *DataLength = 0;
7186                 status = STATUS_INVALID_PARAMETER;
7187             }
7188         }
7189         else
7190         {
7191             status = STATUS_INVALID_PARAMETER;
7192         }
7193     }
7194 
7195     if (NT_SUCCESS(status))
7196     {
7197         status = WdfRequestRetrieveOutputBuffer(Request,
7198                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
7199                                                 &outputBuffer,
7200                                                 NULL);
7201     }
7202 
7203     if (NT_SUCCESS(status))
7204     {
7205         switch (rawReadInfo.TrackMode)
7206         {
7207         case CDDA:
7208         case YellowMode2:
7209         case XAForm2:
7210             // no check needed.
7211             break;
7212 
7213         case RawWithC2AndSubCode:
7214             if (!cdData->Mmc.ReadCdC2Pointers || !cdData->Mmc.ReadCdSubCode)
7215             {
7216                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
7217                           "Request to read C2 & Subcode rejected.  "
7218                           "Is C2 supported: %d   Is Subcode supported: %d\n",
7219                           cdData->Mmc.ReadCdC2Pointers,
7220                           cdData->Mmc.ReadCdSubCode
7221                           ));
7222                 *DataLength = 0;
7223                 status = STATUS_INVALID_DEVICE_REQUEST;
7224             }
7225             break;
7226         case RawWithC2:
7227             if (!cdData->Mmc.ReadCdC2Pointers)
7228             {
7229                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
7230                           "Request to read C2 rejected because drive does not "
7231                           "report support for C2 pointers\n"
7232                           ));
7233                 *DataLength = 0;
7234                 status = STATUS_INVALID_DEVICE_REQUEST;
7235             }
7236             break;
7237         case RawWithSubCode:
7238 
7239             if (!cdData->Mmc.ReadCdSubCode)
7240             {
7241                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
7242                           "Request to read subcode rejected because drive does "
7243                           "not report support for reading the subcode data\n"
7244                           ));
7245                 *DataLength = 0;
7246                 status = STATUS_INVALID_DEVICE_REQUEST;
7247             }
7248             break;
7249 
7250         default:
7251             *DataLength = 0;
7252             status = STATUS_INVALID_DEVICE_REQUEST;
7253             break;
7254         }
7255     }
7256 
7257     if (NT_SUCCESS(status))
7258     {
7259         PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
7260         PCDB                cdb = (PCDB)(srb->Cdb);
7261 
7262         size_t              transferByteCount;
7263         BOOLEAN             shouldRetry = TRUE;
7264         ULONG               timesAlreadyRetried = 0;
7265         LONGLONG            retryIn100nsUnits = 0;
7266 
7267         transferByteCount = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
7268 
7269         ScratchBuffer_BeginUse(DeviceExtension);
7270 
7271         while (shouldRetry)
7272         {
7273             // Setup cdb depending upon the sector type we want.
7274             switch (rawReadInfo.TrackMode)
7275             {
7276             case CDDA:
7277                 transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
7278                 cdb->READ_CD.ExpectedSectorType = CD_DA_SECTOR;
7279                 cdb->READ_CD.IncludeUserData = 1;
7280                 cdb->READ_CD.HeaderCode = 3;
7281                 cdb->READ_CD.IncludeSyncData = 1;
7282                 break;
7283 
7284             case YellowMode2:
7285                 transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
7286                 cdb->READ_CD.ExpectedSectorType = YELLOW_MODE2_SECTOR;
7287                 cdb->READ_CD.IncludeUserData = 1;
7288                 cdb->READ_CD.HeaderCode = 1;
7289                 cdb->READ_CD.IncludeSyncData = 1;
7290                 break;
7291 
7292             case XAForm2:
7293                 transferByteCount  = rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
7294                 cdb->READ_CD.ExpectedSectorType = FORM2_MODE2_SECTOR;
7295                 cdb->READ_CD.IncludeUserData = 1;
7296                 cdb->READ_CD.HeaderCode = 3;
7297                 cdb->READ_CD.IncludeSyncData = 1;
7298                 break;
7299 
7300             case RawWithC2AndSubCode:
7301                 transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE;
7302                 cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
7303                 cdb->READ_CD.IncludeUserData = 1;
7304                 cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
7305                 cdb->READ_CD.IncludeSyncData = 1;
7306                 cdb->READ_CD.ErrorFlags = 2;            // C2 and block error
7307                 cdb->READ_CD.SubChannelSelection = 1;   // raw subchannel data
7308                 break;
7309 
7310             case RawWithC2:
7311                 transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_SIZE;
7312                 cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
7313                 cdb->READ_CD.IncludeUserData = 1;
7314                 cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
7315                 cdb->READ_CD.IncludeSyncData = 1;
7316                 cdb->READ_CD.ErrorFlags = 2;            // C2 and block error
7317                 break;
7318 
7319             case RawWithSubCode:
7320                 transferByteCount  = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_SUBCODE_SIZE;
7321                 cdb->READ_CD.ExpectedSectorType = 0;    // Any sector type
7322                 cdb->READ_CD.IncludeUserData = 1;
7323                 cdb->READ_CD.HeaderCode = 3;            // Header and subheader returned
7324                 cdb->READ_CD.IncludeSyncData = 1;
7325                 cdb->READ_CD.SubChannelSelection = 1;   // raw subchannel data
7326                 break;
7327 
7328             default:
7329                 // should already checked before coming in loop.
7330                 NT_ASSERT(FALSE);
7331                 break;
7332             }
7333 
7334             ScratchBuffer_SetupSrb(DeviceExtension, Request, (ULONG)transferByteCount, TRUE);
7335             // Restore the buffer, buffer size and MdlAddress. They got changed but we don't want to use the scratch buffer.
7336             {
7337                 PIRP    scratchIrp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
7338                 scratchIrp->MdlAddress = irp->MdlAddress;
7339                 srb->DataBuffer = outputVirtAddr;
7340                 srb->DataTransferLength = (ULONG)transferByteCount;
7341             }
7342             // Calculate starting offset.
7343             startingSector = (ULONG)(rawReadInfo.DiskOffset.QuadPart >> DeviceExtension->SectorShift);
7344 
7345             // Fill in CDB fields.
7346             srb->CdbLength = 12;
7347 
7348             cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
7349             cdb->READ_CD.TransferBlocks[2]  = (UCHAR) (rawReadInfo.SectorCount & 0xFF);
7350             cdb->READ_CD.TransferBlocks[1]  = (UCHAR) (rawReadInfo.SectorCount >> 8 );
7351             cdb->READ_CD.TransferBlocks[0]  = (UCHAR) (rawReadInfo.SectorCount >> 16);
7352 
7353             cdb->READ_CD.StartingLBA[3]  = (UCHAR) (startingSector & 0xFF);
7354             cdb->READ_CD.StartingLBA[2]  = (UCHAR) ((startingSector >>  8));
7355             cdb->READ_CD.StartingLBA[1]  = (UCHAR) ((startingSector >> 16));
7356             cdb->READ_CD.StartingLBA[0]  = (UCHAR) ((startingSector >> 24));
7357 
7358             ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL);
7359 
7360             if ((DeviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
7361                 (DeviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
7362             {
7363                 shouldRetry = FALSE;
7364                 status = STATUS_CANCELLED;
7365             }
7366             else
7367             {
7368                 shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension,
7369                                                                         timesAlreadyRetried,
7370                                                                         &status,
7371                                                                         &retryIn100nsUnits);
7372                 if (shouldRetry)
7373                 {
7374                     LARGE_INTEGER t;
7375                     t.QuadPart = -retryIn100nsUnits;
7376                     timesAlreadyRetried++;
7377                     KeDelayExecutionThread(KernelMode, FALSE, &t);
7378                     // keep items clean
7379                     ScratchBuffer_ResetItems(DeviceExtension, FALSE);
7380                 }
7381             }
7382         }
7383 
7384         if (NT_SUCCESS(status))
7385         {
7386             *DataLength = srb->DataTransferLength;
7387         }
7388 
7389         ScratchBuffer_EndUse(DeviceExtension);
7390     }
7391 
7392     return status;
7393 }
7394 
_IRQL_requires_max_(APC_LEVEL)7395 _IRQL_requires_max_(APC_LEVEL)
7396 NTSTATUS
7397 DeviceHandlePlayAudioMsf(
7398     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7399     _In_  WDFREQUEST               Request,
7400     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
7401     _Out_ size_t *                 DataLength
7402     )
7403 /*++
7404 
7405 Routine Description:
7406 
7407    Handle request of IOCTL_CDROM_PLAY_AUDIO_MSF
7408 
7409 Arguments:
7410 
7411     DeviceExtension - device context
7412     Request - request to be handled
7413     RequestParameters - request parameter
7414     DataLength - transferred data length
7415 
7416 Return Value:
7417 
7418     NTSTATUS
7419 
7420 --*/
7421 {
7422     NTSTATUS                status = STATUS_SUCCESS;
7423     PCDROM_PLAY_AUDIO_MSF   inputBuffer = NULL;
7424 
7425     PAGED_CODE ();
7426 
7427     *DataLength = 0;
7428 
7429     status = WdfRequestRetrieveInputBuffer(Request,
7430                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
7431                                            (PVOID*)&inputBuffer,
7432                                            NULL);
7433 
7434 
7435     if (NT_SUCCESS(status))
7436     {
7437         ULONG   transferSize = 0;
7438         CDB     cdb;
7439 
7440         ScratchBuffer_BeginUse(DeviceExtension);
7441 
7442         RtlZeroMemory(&cdb, sizeof(CDB));
7443         // Set up the CDB
7444         cdb.PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF;
7445 
7446         cdb.PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM;
7447         cdb.PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS;
7448         cdb.PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF;
7449 
7450         cdb.PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM;
7451         cdb.PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS;
7452         cdb.PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF;
7453 
7454         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);
7455 
7456         if (NT_SUCCESS(status))
7457         {
7458             PLAY_ACTIVE(DeviceExtension) = TRUE;
7459             *DataLength = 0;
7460         }
7461 
7462         // nothing to do after the command finishes.
7463         ScratchBuffer_EndUse(DeviceExtension);
7464     }
7465 
7466     return status;
7467 }
7468 
_IRQL_requires_max_(APC_LEVEL)7469 _IRQL_requires_max_(APC_LEVEL)
7470 NTSTATUS
7471 DeviceHandleReadQChannel(
7472     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7473     _In_  WDFREQUEST               Request,
7474     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
7475     _Out_ size_t *                 DataLength
7476     )
7477 /*++
7478 
7479 Routine Description:
7480 
7481    Handle request of IOCTL_CDROM_READ_Q_CHANNEL
7482 
7483 Arguments:
7484 
7485     DeviceExtension - device context
7486     Request - request to be handled
7487     RequestParameters - request parameter
7488     DataLength - transferred data length
7489 
7490 Return Value:
7491 
7492     NTSTATUS
7493 
7494 --*/
7495 {
7496     NTSTATUS    status = STATUS_SUCCESS;
7497     PVOID       inputBuffer = NULL;
7498     PVOID       outputBuffer = NULL;
7499 
7500     PAGED_CODE ();
7501 
7502     *DataLength = 0;
7503 
7504     status = WdfRequestRetrieveInputBuffer(Request,
7505                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
7506                                            &inputBuffer,
7507                                            NULL);
7508 
7509     if (NT_SUCCESS(status))
7510     {
7511         status = WdfRequestRetrieveOutputBuffer(Request,
7512                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
7513                                                 &outputBuffer,
7514                                                 NULL);
7515     }
7516 
7517     if (NT_SUCCESS(status))
7518     {
7519         status = ReadQChannel(DeviceExtension,
7520                               Request,
7521                               inputBuffer,
7522                               RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
7523                               outputBuffer,
7524                               RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
7525                               DataLength);
7526     }
7527 
7528     return status;
7529 }
7530 
_IRQL_requires_max_(APC_LEVEL)7531 _IRQL_requires_max_(APC_LEVEL)
7532 NTSTATUS
7533 ReadQChannel(
7534     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7535     _In_opt_  WDFREQUEST           OriginalRequest,
7536     _In_  PVOID                    InputBuffer,
7537     _In_  size_t                   InputBufferLength,
7538     _In_  PVOID                    OutputBuffer,
7539     _In_  size_t                   OutputBufferLength,
7540     _Out_ size_t *                 DataLength
7541     )
7542 /*++
7543 
7544 Routine Description:
7545 
7546    base function to handle request of IOCTL_CDROM_READ_Q_CHANNEL
7547 
7548 Arguments:
7549 
7550     DeviceExtension - device context
7551     OriginalRequest - original request to be handled
7552     InputBuffer - input buffer
7553     InputBufferLength - length of input buffer
7554     OutputBuffer - output buffer
7555     OutputBufferLength - length of output buffer
7556 
7557 Return Value:
7558 
7559     NTSTATUS
7560     DataLength - returned data length
7561 
7562 --*/
7563 {
7564     NTSTATUS                    status = STATUS_SUCCESS;
7565     ULONG                       transferByteCount = 0;
7566     CDB                         cdb;
7567     PCDROM_SUB_Q_DATA_FORMAT    inputBuffer = (PCDROM_SUB_Q_DATA_FORMAT)InputBuffer;
7568     PSUB_Q_CHANNEL_DATA         userChannelData = (PSUB_Q_CHANNEL_DATA)OutputBuffer;
7569 
7570     PAGED_CODE ();
7571 
7572     UNREFERENCED_PARAMETER(InputBufferLength);
7573 
7574     *DataLength = 0;
7575 
7576     // Set size of channel data -- however, this is dependent on
7577     // what information we are requesting (which Format)
7578     switch( inputBuffer->Format )
7579     {
7580         case IOCTL_CDROM_CURRENT_POSITION:
7581             transferByteCount = sizeof(SUB_Q_CURRENT_POSITION);
7582             break;
7583 
7584         case IOCTL_CDROM_MEDIA_CATALOG:
7585             transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
7586             break;
7587 
7588         case IOCTL_CDROM_TRACK_ISRC:
7589             transferByteCount = sizeof(SUB_Q_TRACK_ISRC);
7590             break;
7591     }
7592 
7593     ScratchBuffer_BeginUse(DeviceExtension);
7594 
7595     RtlZeroMemory(&cdb, sizeof(CDB));
7596     // Set up the CDB
7597     // Always logical unit 0, but only use MSF addressing
7598     // for IOCTL_CDROM_CURRENT_POSITION
7599     if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION)
7600     {
7601         cdb.SUBCHANNEL.Msf = CDB_USE_MSF;
7602     }
7603 
7604     // Return subchannel data
7605     cdb.SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK;
7606 
7607     // Specify format of informatin to return
7608     cdb.SUBCHANNEL.Format = inputBuffer->Format;
7609 
7610     // Specify which track to access (only used by Track ISRC reads)
7611     if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC)
7612     {
7613         cdb.SUBCHANNEL.TrackNumber = inputBuffer->Track;
7614     }
7615 
7616     cdb.SUBCHANNEL.AllocationLength[0] = (UCHAR) (transferByteCount >> 8);
7617     cdb.SUBCHANNEL.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF);
7618     cdb.SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL;
7619 
7620     status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, transferByteCount, TRUE, &cdb, 10);
7621 
7622     if (NT_SUCCESS(status))
7623     {
7624         PSUB_Q_CHANNEL_DATA subQPtr = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;
7625 
7626 #if DBG
7627         switch( inputBuffer->Format )
7628         {
7629         case IOCTL_CDROM_CURRENT_POSITION:
7630             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus ));
7631             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR ));
7632             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Control = 0x%x\n", subQPtr->CurrentPosition.Control ));
7633             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Track = %u\n", subQPtr->CurrentPosition.TrackNumber ));
7634             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Index = %u\n", subQPtr->CurrentPosition.IndexNumber ));
7635             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) ));
7636             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) ));
7637             break;
7638 
7639         case IOCTL_CDROM_MEDIA_CATALOG:
7640             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus ));
7641             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Mcval is %u\n", subQPtr->MediaCatalog.Mcval ));
7642             break;
7643 
7644         case IOCTL_CDROM_TRACK_ISRC:
7645             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus ));
7646             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Tcval is %u\n", subQPtr->TrackIsrc.Tcval ));
7647             break;
7648         }
7649 #endif
7650 
7651         // Update the play active status.
7652         if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS)
7653         {
7654             PLAY_ACTIVE(DeviceExtension) = TRUE;
7655         }
7656         else
7657         {
7658             PLAY_ACTIVE(DeviceExtension) = FALSE;
7659         }
7660 
7661         // Check if output buffer is large enough to contain
7662         // the data.
7663         if (OutputBufferLength < DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength)
7664         {
7665             DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength = (ULONG)OutputBufferLength;
7666         }
7667 
7668         // Copy our buffer into users.
7669         RtlMoveMemory(userChannelData,
7670                       subQPtr,
7671                       DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
7672 
7673         *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
7674     }
7675 
7676     // nothing to do after the command finishes.
7677     ScratchBuffer_EndUse(DeviceExtension);
7678 
7679     return status;
7680 }
7681 
7682 
_IRQL_requires_max_(APC_LEVEL)7683 _IRQL_requires_max_(APC_LEVEL)
7684 NTSTATUS
7685 DeviceHandlePauseAudio(
7686     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7687     _In_  WDFREQUEST               Request,
7688     _Out_ size_t *                 DataLength
7689     )
7690 /*++
7691 
7692 Routine Description:
7693 
7694    Handle request of IOCTL_CDROM_PAUSE_AUDIO
7695 
7696 Arguments:
7697 
7698     DeviceExtension - device context
7699     Request - request to be handled
7700     DataLength - transferred data length
7701 
7702 Return Value:
7703 
7704     NTSTATUS
7705 
7706 --*/
7707 {
7708     NTSTATUS                status = STATUS_SUCCESS;
7709 
7710     PAGED_CODE ();
7711 
7712     *DataLength = 0;
7713 
7714     if (NT_SUCCESS(status))
7715     {
7716         ULONG   transferSize = 0;
7717         CDB     cdb;
7718 
7719         ScratchBuffer_BeginUse(DeviceExtension);
7720 
7721         RtlZeroMemory(&cdb, sizeof(CDB));
7722         // Set up the CDB
7723         cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
7724         cdb.PAUSE_RESUME.Action = CDB_AUDIO_PAUSE;
7725 
7726         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);
7727 
7728         if (NT_SUCCESS(status))
7729         {
7730             PLAY_ACTIVE(DeviceExtension) = FALSE;
7731             *DataLength = 0;
7732         }
7733 
7734         // nothing to do after the command finishes.
7735         ScratchBuffer_EndUse(DeviceExtension);
7736     }
7737 
7738     return status;
7739 }
7740 
_IRQL_requires_max_(APC_LEVEL)7741 _IRQL_requires_max_(APC_LEVEL)
7742 NTSTATUS
7743 DeviceHandleResumeAudio(
7744     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7745     _In_  WDFREQUEST               Request,
7746     _Out_ size_t *                 DataLength
7747     )
7748 /*++
7749 
7750 Routine Description:
7751 
7752    Handle request of IOCTL_CDROM_RESUME_AUDIO
7753 
7754 Arguments:
7755 
7756     DeviceExtension - device context
7757     Request - request to be handled
7758     DataLength - transferred data length
7759 
7760 Return Value:
7761 
7762     NTSTATUS
7763 
7764 --*/
7765 {
7766     NTSTATUS                status = STATUS_SUCCESS;
7767 
7768     PAGED_CODE ();
7769 
7770     *DataLength = 0;
7771 
7772     if (NT_SUCCESS(status))
7773     {
7774         ULONG   transferSize = 0;
7775         CDB     cdb;
7776 
7777         ScratchBuffer_BeginUse(DeviceExtension);
7778 
7779         RtlZeroMemory(&cdb, sizeof(CDB));
7780         // Set up the CDB
7781         cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
7782         cdb.PAUSE_RESUME.Action = CDB_AUDIO_RESUME;
7783 
7784         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);
7785 
7786         if (NT_SUCCESS(status))
7787         {
7788             PLAY_ACTIVE(DeviceExtension) = TRUE;  //not in original code. But we should set it.
7789             *DataLength = 0;
7790         }
7791 
7792         // nothing to do after the command finishes.
7793         ScratchBuffer_EndUse(DeviceExtension);
7794     }
7795 
7796     return status;
7797 }
7798 
_IRQL_requires_max_(APC_LEVEL)7799 _IRQL_requires_max_(APC_LEVEL)
7800 NTSTATUS
7801 DeviceHandleSeekAudioMsf(
7802     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7803     _In_  WDFREQUEST               Request,
7804     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
7805     _Out_ size_t *                 DataLength
7806     )
7807 /*++
7808 
7809 Routine Description:
7810 
7811    Handle request of IOCTL_CDROM_SEEK_AUDIO_MSF
7812 
7813 Arguments:
7814 
7815     DeviceExtension - device context
7816     Request - request to be handled
7817     RequestParameters - request parameter
7818     DataLength - transferred data length
7819 
7820 Return Value:
7821 
7822     NTSTATUS
7823 
7824 --*/
7825 {
7826     NTSTATUS              status = STATUS_SUCCESS;
7827     PCDROM_SEEK_AUDIO_MSF inputBuffer = NULL;
7828 
7829     PAGED_CODE ();
7830 
7831     *DataLength = 0;
7832 
7833     status = WdfRequestRetrieveInputBuffer(Request,
7834                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
7835                                            (PVOID*)&inputBuffer,
7836                                            NULL);
7837 
7838     if (NT_SUCCESS(status))
7839     {
7840         ULONG   transferSize = 0;
7841         CDB     cdb;
7842         ULONG   logicalBlockAddress;
7843 
7844         logicalBlockAddress = MSF_TO_LBA(inputBuffer->M, inputBuffer->S, inputBuffer->F);
7845 
7846         ScratchBuffer_BeginUse(DeviceExtension);
7847 
7848         RtlZeroMemory(&cdb, sizeof(CDB));
7849         // Set up the CDB
7850         cdb.SEEK.OperationCode      = SCSIOP_SEEK;
7851         cdb.SEEK.LogicalBlockAddress[0] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3;
7852         cdb.SEEK.LogicalBlockAddress[1] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2;
7853         cdb.SEEK.LogicalBlockAddress[2] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1;
7854         cdb.SEEK.LogicalBlockAddress[3] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0;
7855 
7856         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10);
7857 
7858         if (NT_SUCCESS(status))
7859         {
7860             *DataLength = 0;
7861         }
7862 
7863         // nothing to do after the command finishes.
7864 
7865         ScratchBuffer_EndUse(DeviceExtension);
7866     }
7867 
7868     return status;
7869 }
7870 
_IRQL_requires_max_(APC_LEVEL)7871 _IRQL_requires_max_(APC_LEVEL)
7872 NTSTATUS
7873 DeviceHandleStopAudio(
7874     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7875     _In_  WDFREQUEST               Request,
7876     _Out_ size_t *                 DataLength
7877     )
7878 {
7879     NTSTATUS                status = STATUS_SUCCESS;
7880 
7881     PAGED_CODE ();
7882 
7883     *DataLength = 0;
7884 
7885     if (NT_SUCCESS(status))
7886     {
7887         ULONG   transferSize = 0;
7888         CDB     cdb;
7889 
7890         ScratchBuffer_BeginUse(DeviceExtension);
7891 
7892         RtlZeroMemory(&cdb, sizeof(CDB));
7893         // Set up the CDB
7894         cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
7895         cdb.START_STOP.Immediate = 1;
7896         cdb.START_STOP.Start = 0;
7897         cdb.START_STOP.LoadEject = 0;
7898 
7899         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 6);
7900 
7901         if (NT_SUCCESS(status))
7902         {
7903             PLAY_ACTIVE(DeviceExtension) = FALSE;
7904             *DataLength = 0;
7905         }
7906 
7907         // nothing to do after the command finishes.
7908         ScratchBuffer_EndUse(DeviceExtension);
7909     }
7910 
7911     return status;
7912 }
7913 
_IRQL_requires_max_(APC_LEVEL)7914 _IRQL_requires_max_(APC_LEVEL)
7915 NTSTATUS
7916 DeviceHandleGetSetVolume(
7917     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
7918     _In_  WDFREQUEST               Request,
7919     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
7920     _Out_ size_t *                 DataLength
7921     )
7922 /*++
7923 
7924 Routine Description:
7925 
7926    Handle request of IOCTL_CDROM_GET_VOLUME
7927                      IOCTL_CDROM_SET_VOLUME
7928 
7929 Arguments:
7930 
7931     DeviceExtension - device context
7932     Request - request to be handled
7933     RequestParameters - request parameter
7934     DataLength - transferred data length
7935 
7936 Return Value:
7937 
7938     NTSTATUS
7939 
7940 --*/
7941 {
7942     NTSTATUS              status = STATUS_SUCCESS;
7943 
7944     PAGED_CODE ();
7945 
7946     *DataLength = 0;
7947 
7948     if (NT_SUCCESS(status))
7949     {
7950         ULONG   transferSize = MODE_DATA_SIZE;
7951         CDB     cdb;
7952 
7953         ScratchBuffer_BeginUse(DeviceExtension);
7954 
7955         RtlZeroMemory(&cdb, sizeof(CDB));
7956         // Set up the CDB
7957         cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
7958         cdb.MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE;
7959         cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8);
7960         cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF);
7961 
7962         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, TRUE, &cdb, 10);
7963 
7964         if (NT_SUCCESS(status))
7965         {
7966             if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME)
7967             {
7968                 PAUDIO_OUTPUT   audioOutput;
7969                 PVOLUME_CONTROL volumeControl;
7970                 ULONG           bytesTransferred;
7971 
7972                 status = WdfRequestRetrieveOutputBuffer(Request,
7973                                                         RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
7974                                                         (PVOID*)&volumeControl,
7975                                                         NULL);
7976                 if (NT_SUCCESS(status))
7977                 {
7978                     audioOutput = ModeSenseFindSpecificPage((PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
7979                                                             DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
7980                                                             CDROM_AUDIO_CONTROL_PAGE,
7981                                                             FALSE);
7982 
7983                     // Verify the page is as big as expected.
7984                     bytesTransferred = (ULONG)((PCHAR)audioOutput - (PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer) +
7985                                         sizeof(AUDIO_OUTPUT);
7986 
7987                     if ((audioOutput != NULL) &&
7988                         (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength >= bytesTransferred))
7989                     {
7990                         ULONG i;
7991                         for (i=0; i<4; i++)
7992                         {
7993                             volumeControl->PortVolume[i] = audioOutput->PortOutput[i].Volume;
7994                         }
7995 
7996                         // Set bytes transferred in IRP.
7997                         *DataLength = sizeof(VOLUME_CONTROL);
7998 
7999                     }
8000                     else
8001                     {
8002                         *DataLength = 0;
8003                         status = STATUS_INVALID_DEVICE_REQUEST;
8004                     }
8005                 }
8006             }
8007             else    //IOCTL_CDROM_SET_VOLUME
8008             {
8009                 PAUDIO_OUTPUT   audioInput = NULL;
8010                 PAUDIO_OUTPUT   audioOutput = NULL;
8011                 PVOLUME_CONTROL volumeControl = NULL;
8012                 ULONG           i,bytesTransferred,headerLength;
8013 
8014                 status = WdfRequestRetrieveInputBuffer(Request,
8015                                                        RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8016                                                        (PVOID*)&volumeControl,
8017                                                        NULL);
8018 
8019                 if (NT_SUCCESS(status))
8020                 {
8021                     audioInput = ModeSenseFindSpecificPage((PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
8022                                                            DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
8023                                                            CDROM_AUDIO_CONTROL_PAGE,
8024                                                            FALSE);
8025 
8026                     // Check to make sure the mode sense data is valid before we go on
8027                     if(audioInput == NULL)
8028                     {
8029                         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
8030                                     "Mode Sense Page %d not found\n",
8031                                     CDROM_AUDIO_CONTROL_PAGE));
8032 
8033                         *DataLength = 0;
8034                         status = STATUS_INVALID_DEVICE_REQUEST;
8035                     }
8036                 }
8037 
8038                 if (NT_SUCCESS(status))
8039                 {
8040                     // keep items clean; clear the command history
8041                     ScratchBuffer_ResetItems(DeviceExtension, TRUE);
8042 
8043                     headerLength = sizeof(MODE_PARAMETER_HEADER10);
8044                     bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength;
8045 
8046                     // use the scratch buffer as input buffer.
8047                     // the content of this buffer will not be changed in the following loop.
8048                     audioOutput = (PAUDIO_OUTPUT)((PCHAR)DeviceExtension->ScratchContext.ScratchBuffer + headerLength);
8049 
8050                     for (i=0; i<4; i++)
8051                     {
8052                         audioOutput->PortOutput[i].Volume = volumeControl->PortVolume[i];
8053                         audioOutput->PortOutput[i].ChannelSelection = audioInput->PortOutput[i].ChannelSelection;
8054                     }
8055 
8056                     audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE;
8057                     audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2;
8058                     audioOutput->Immediate = MODE_SELECT_IMMEDIATE;
8059 
8060                     RtlZeroMemory(&cdb, sizeof(CDB));
8061                     // Set up the CDB
8062                     cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
8063                     cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8);
8064                     cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF);
8065                     cdb.MODE_SELECT10.PFBit = 1;
8066 
8067                     status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, bytesTransferred, FALSE, &cdb, 10);
8068 
8069                 }
8070                 *DataLength = 0;
8071             }
8072         }
8073 
8074         // nothing to do after the command finishes.
8075         ScratchBuffer_EndUse(DeviceExtension);
8076     }
8077 
8078     return status;
8079 }
8080 
_IRQL_requires_max_(APC_LEVEL)8081 _IRQL_requires_max_(APC_LEVEL)
8082 NTSTATUS
8083 DeviceHandleReadDvdStructure(
8084     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
8085     _In_  WDFREQUEST               Request,
8086     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
8087     _Out_ size_t *                 DataLength
8088     )
8089 /*++
8090 
8091 Routine Description:
8092 
8093    Handle request of IOCTL_DVD_READ_STRUCTURE
8094 
8095 Arguments:
8096 
8097     DeviceExtension - device context
8098     Request - request to be handled
8099     RequestParameters - request parameter
8100     DataLength - transferred data length
8101 
8102 Return Value:
8103 
8104     NTSTATUS
8105 
8106 --*/
8107 {
8108     NTSTATUS    status = STATUS_SUCCESS;
8109     PVOID       inputBuffer = NULL;
8110     PVOID       outputBuffer = NULL;
8111 
8112     PAGED_CODE ();
8113 
8114     *DataLength = 0;
8115 
8116     status = WdfRequestRetrieveInputBuffer(Request,
8117                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8118                                            &inputBuffer,
8119                                            NULL);
8120 
8121     if (NT_SUCCESS(status))
8122     {
8123         status = WdfRequestRetrieveOutputBuffer(Request,
8124                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
8125                                                 &outputBuffer,
8126                                                 NULL);
8127     }
8128 
8129     if (NT_SUCCESS(status))
8130     {
8131         status = ReadDvdStructure(DeviceExtension,
8132                                   Request,
8133                                   inputBuffer,
8134                                   RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8135                                   outputBuffer,
8136                                   RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
8137                                   DataLength);
8138     }
8139 
8140     return status;
8141 }
8142 
_IRQL_requires_max_(APC_LEVEL)8143 _IRQL_requires_max_(APC_LEVEL)
8144 NTSTATUS
8145 ReadDvdStructure(
8146     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
8147     _In_opt_  WDFREQUEST           OriginalRequest,
8148     _In_  PVOID                    InputBuffer,
8149     _In_  size_t                   InputBufferLength,
8150     _In_  PVOID                    OutputBuffer,
8151     _In_  size_t                   OutputBufferLength,
8152     _Out_ size_t *                 DataLength
8153     )
8154 /*++
8155 
8156 Routine Description:
8157 
8158    base function to handle request of IOCTL_DVD_START_SESSION
8159                                       IOCTL_DVD_READ_KEY
8160 
8161 Arguments:
8162 
8163     DeviceExtension - device context
8164     OriginalRequest - original request to be handled
8165     InputBuffer - input buffer
8166     InputBufferLength - length of input buffer
8167     OutputBuffer - output buffer
8168     OutputBufferLength - length of output buffer
8169 
8170 Return Value:
8171 
8172     NTSTATUS
8173     DataLength - returned data length
8174 
8175 --*/
8176 {
8177     NTSTATUS                status = STATUS_SUCCESS;
8178     PDVD_READ_STRUCTURE     request = (PDVD_READ_STRUCTURE)InputBuffer;
8179     PDVD_DESCRIPTOR_HEADER  header = (PDVD_DESCRIPTOR_HEADER)OutputBuffer;
8180     CDB                     cdb;
8181 
8182     USHORT                  dataLength;
8183     ULONG                   blockNumber;
8184     PFOUR_BYTE              fourByte;
8185 
8186     PAGED_CODE ();
8187 
8188     UNREFERENCED_PARAMETER(InputBufferLength);
8189 
8190     if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
8191     {
8192         *DataLength = 0;
8193         return STATUS_INVALID_DEVICE_REQUEST;
8194     }
8195 
8196     dataLength = (USHORT)OutputBufferLength;
8197     blockNumber = (ULONG)(request->BlockByteOffset.QuadPart >> DeviceExtension->SectorShift);
8198     fourByte = (PFOUR_BYTE)&blockNumber;
8199 
8200     ScratchBuffer_BeginUse(DeviceExtension);
8201 
8202     RtlZeroMemory(&cdb, sizeof(CDB));
8203     // Set up the CDB
8204     cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
8205     cdb.READ_DVD_STRUCTURE.RMDBlockNumber[0] = fourByte->Byte3;
8206     cdb.READ_DVD_STRUCTURE.RMDBlockNumber[1] = fourByte->Byte2;
8207     cdb.READ_DVD_STRUCTURE.RMDBlockNumber[2] = fourByte->Byte1;
8208     cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3] = fourByte->Byte0;
8209     cdb.READ_DVD_STRUCTURE.LayerNumber   = request->LayerNumber;
8210     cdb.READ_DVD_STRUCTURE.Format        = (UCHAR)request->Format;
8211 
8212 #if DBG
8213     {
8214         if ((UCHAR)request->Format > DvdMaxDescriptor)
8215         {
8216             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
8217                         "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n",
8218                         (UCHAR)request->Format,
8219                         READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor],
8220                         dataLength
8221                         ));
8222         }
8223         else
8224         {
8225             TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
8226                         "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n",
8227                         (UCHAR)request->Format,
8228                         READ_DVD_STRUCTURE_FORMAT_STRINGS[(UCHAR)request->Format],
8229                         dataLength
8230                         ));
8231         }
8232     }
8233 #endif // DBG
8234 
8235     if (request->Format == DvdDiskKeyDescriptor)
8236     {
8237         cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)request->SessionId;
8238     }
8239 
8240     cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataLength >> 8);
8241     cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataLength & 0xff);
8242 
8243     status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, dataLength, TRUE, &cdb, 12);
8244 
8245     if (NT_SUCCESS(status))
8246     {
8247         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
8248                     "DvdDCCompletion - READ_STRUCTURE: descriptor format of %d\n", request->Format));
8249 
8250         RtlMoveMemory(header,
8251                       DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
8252                       DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
8253 
8254         // Cook the data.  There are a number of fields that really
8255         // should be byte-swapped for the caller.
8256         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
8257                   "DvdDCCompletion - READ_STRUCTURE:\n"
8258                   "\tHeader at %p\n"
8259                   "\tDvdDCCompletion - READ_STRUCTURE: data at %p\n"
8260                   "\tDataBuffer was at %p\n"
8261                   "\tDataTransferLength was %lx\n",
8262                   header,
8263                   header->Data,
8264                   DeviceExtension->ScratchContext.ScratchSrb->DataBuffer,
8265                   DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength));
8266 
8267         // First the fields in the header
8268         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: header->Length %lx -> ",
8269                    header->Length));
8270 
8271         REVERSE_SHORT(&header->Length);
8272 
8273         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", header->Length));
8274 
8275         // Now the fields in the descriptor
8276         if(request->Format == DvdPhysicalDescriptor)
8277         {
8278             ULONG tempLength = (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength > (ULONG)FIELD_OFFSET(DVD_DESCRIPTOR_HEADER, Data))
8279                                 ? (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength - FIELD_OFFSET(DVD_DESCRIPTOR_HEADER, Data))
8280                                 : 0;
8281 
8282             PDVD_LAYER_DESCRIPTOR layer = (PDVD_LAYER_DESCRIPTOR)&(header->Data[0]);
8283 
8284             // Make sure the buffer size is good for swapping bytes.
8285             if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, StartingDataSector))
8286             {
8287                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: StartingDataSector %lx -> ",
8288                                layer->StartingDataSector));
8289                 REVERSE_LONG(&(layer->StartingDataSector));
8290                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->StartingDataSector));
8291             }
8292             if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, EndDataSector))
8293             {
8294                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: EndDataSector %lx -> ",
8295                                layer->EndDataSector));
8296                 REVERSE_LONG(&(layer->EndDataSector));
8297                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->EndDataSector));
8298             }
8299             if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, EndLayerZeroSector))
8300             {
8301                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: EndLayerZeroSector %lx -> ",
8302                                layer->EndLayerZeroSector));
8303                 REVERSE_LONG(&(layer->EndLayerZeroSector));
8304                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->EndLayerZeroSector));
8305             }
8306         }
8307 
8308         if (!NT_SUCCESS(status))
8309         {
8310             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "Status is %lx\n", status));
8311             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdDeviceControlCompletion - "
8312                         "IOCTL_DVD_READ_STRUCTURE: data transfer length of %d\n",
8313                         DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength));
8314         }
8315 
8316         *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
8317     }
8318 
8319     ScratchBuffer_EndUse(DeviceExtension);
8320 
8321     return status;
8322 }
8323 
_IRQL_requires_max_(APC_LEVEL)8324 _IRQL_requires_max_(APC_LEVEL)
8325 NTSTATUS
8326 DeviceHandleDvdEndSession(
8327     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
8328     _In_  WDFREQUEST               Request,
8329     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
8330     _Out_ size_t *                 DataLength
8331     )
8332 /*++
8333 
8334 Routine Description:
8335 
8336    Handle request of IOCTL_DVD_END_SESSION
8337 
8338 Arguments:
8339 
8340     DeviceExtension - device context
8341     Request - request to be handled
8342     RequestParameters - request parameter
8343     DataLength - transferred data length
8344 
8345 Return Value:
8346 
8347     NTSTATUS
8348 
8349 --*/
8350 {
8351     NTSTATUS        status = STATUS_SUCCESS;
8352     PDVD_SESSION_ID sessionId = NULL;
8353 
8354     PAGED_CODE ();
8355 
8356     *DataLength = 0;
8357 
8358     status = WdfRequestRetrieveInputBuffer(Request,
8359                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8360                                            (PVOID*)&sessionId,
8361                                            NULL);
8362 
8363     if (NT_SUCCESS(status))
8364     {
8365         ULONG           transferSize = 0;
8366         CDB             cdb;
8367         DVD_SESSION_ID  currentSession = 0;
8368         DVD_SESSION_ID  limitSession = 0;
8369 
8370         if(*sessionId == DVD_END_ALL_SESSIONS)
8371         {
8372             currentSession = 0;
8373             limitSession = MAX_COPY_PROTECT_AGID - 1;
8374         }
8375         else
8376         {
8377             currentSession = *sessionId;
8378             limitSession = *sessionId;
8379         }
8380 
8381         ScratchBuffer_BeginUse(DeviceExtension);
8382 
8383         do
8384         {
8385             RtlZeroMemory(&cdb, sizeof(CDB));
8386             // Set up the CDB
8387             cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
8388             cdb.SEND_KEY.AGID = (UCHAR)(currentSession);
8389             cdb.SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID;
8390 
8391             status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);
8392 
8393             currentSession++;
8394         } while ((currentSession <= limitSession) && NT_SUCCESS(status));
8395 
8396         // nothing to do after the command finishes.
8397         ScratchBuffer_EndUse(DeviceExtension);
8398     }
8399 
8400     return status;
8401 }
8402 
_IRQL_requires_max_(APC_LEVEL)8403 _IRQL_requires_max_(APC_LEVEL)
8404 NTSTATUS
8405 DeviceHandleDvdStartSessionReadKey(
8406     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
8407     _In_  WDFREQUEST               Request,
8408     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
8409     _Out_ size_t *                 DataLength
8410     )
8411 /*++
8412 
8413 Routine Description:
8414 
8415    Handle request of IOCTL_DVD_START_SESSION
8416                      IOCTL_DVD_READ_KEY
8417 
8418 Arguments:
8419 
8420     DeviceExtension - device context
8421     Request - request to be handled
8422     RequestParameters - request parameter
8423     DataLength - transferred data length
8424 
8425 Return Value:
8426 
8427     NTSTATUS
8428 
8429 --*/
8430 {
8431     NTSTATUS                status = STATUS_SUCCESS;
8432     PDVD_COPY_PROTECT_KEY   keyParameters = NULL;
8433     PVOID                   outputBuffer = NULL;
8434 
8435     PAGED_CODE ();
8436 
8437     if (NT_SUCCESS(status))
8438     {
8439         status = WdfRequestRetrieveOutputBuffer(Request,
8440                                                 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
8441                                                 &outputBuffer,
8442                                                 NULL);
8443     }
8444 
8445     if (NT_SUCCESS(status) && RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_READ_KEY)
8446     {
8447         status = WdfRequestRetrieveInputBuffer(Request,
8448                                                RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8449                                                (PVOID*)&keyParameters,
8450                                                NULL);
8451     }
8452 
8453     if (NT_SUCCESS(status))
8454     {
8455         status = DvdStartSessionReadKey(DeviceExtension,
8456                                         RequestParameters.Parameters.DeviceIoControl.IoControlCode,
8457                                         Request,
8458                                         keyParameters,
8459                                         RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8460                                         outputBuffer,
8461                                         RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
8462                                         DataLength);
8463     }
8464 
8465     return status;
8466 }
8467 
_IRQL_requires_max_(APC_LEVEL)8468 _IRQL_requires_max_(APC_LEVEL)
8469 NTSTATUS
8470 DvdStartSessionReadKey(
8471     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
8472     _In_  ULONG                    IoControlCode,
8473     _In_opt_  WDFREQUEST           OriginalRequest,
8474     _In_opt_  PVOID                InputBuffer,
8475     _In_  size_t                   InputBufferLength,
8476     _In_  PVOID                    OutputBuffer,
8477     _In_  size_t                   OutputBufferLength,
8478     _Out_ size_t *                 DataLength
8479     )
8480 /*++
8481 
8482 Routine Description:
8483 
8484    base function to handle request of IOCTL_DVD_START_SESSION
8485                                       IOCTL_DVD_READ_KEY
8486 
8487 Arguments:
8488 
8489     DeviceExtension - device context
8490     IoControlCode - IOCTL_DVD_READ_KEY or IOCTL_DVD_START_SESSION
8491     OriginalRequest - original request to be handled
8492     InputBuffer - input buffer
8493     InputBufferLength - length of input buffer
8494     OutputBuffer - output buffer
8495     OutputBufferLength - length of output buffer
8496 
8497 Return Value:
8498 
8499     NTSTATUS
8500     DataLength - returned data length
8501 
8502 --*/
8503 {
8504     NTSTATUS                status = STATUS_SUCCESS;
8505     ULONG                   keyLength = 0;
8506     ULONG                   result = 0;
8507     ULONG                   allocationLength;
8508     PFOUR_BYTE              fourByte;
8509     PDVD_COPY_PROTECT_KEY   keyParameters = (PDVD_COPY_PROTECT_KEY)InputBuffer;
8510 
8511     PAGED_CODE ();
8512 
8513     UNREFERENCED_PARAMETER(InputBufferLength);
8514 
8515     *DataLength = 0;
8516 
8517     fourByte = (PFOUR_BYTE)&allocationLength;
8518 
8519     if (IoControlCode == IOCTL_DVD_READ_KEY)
8520     {
8521         if (keyParameters == NULL)
8522         {
8523             status = STATUS_INTERNAL_ERROR;
8524         }
8525 
8526         // First of all, initialize the DVD region of the drive, if it has not been set yet
8527         if (NT_SUCCESS(status) &&
8528             (keyParameters->KeyType == DvdGetRpcKey) &&
8529             DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd)
8530         {
8531             DevicePickDvdRegion(DeviceExtension->Device);
8532         }
8533 
8534         if (NT_SUCCESS(status) &&
8535             (keyParameters->KeyType == DvdDiskKey))
8536         {
8537             // Special case - need to use READ DVD STRUCTURE command to get the disk key.
8538             PDVD_COPY_PROTECT_KEY   keyHeader = NULL;
8539             PDVD_READ_STRUCTURE     readStructureRequest = (PDVD_READ_STRUCTURE)keyParameters;
8540 
8541             // save the key header so we can restore the interesting parts later
8542             keyHeader = ExAllocatePoolWithTag(NonPagedPoolNx,
8543                                               sizeof(DVD_COPY_PROTECT_KEY),
8544                                               DVD_TAG_READ_KEY);
8545 
8546             if(keyHeader == NULL)
8547             {
8548                 // Can't save the context so return an error
8549                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
8550                             "DvdDeviceControl - READ_KEY: unable to allocate context\n"));
8551                 status = STATUS_INSUFFICIENT_RESOURCES;
8552             }
8553 
8554             if (NT_SUCCESS(status))
8555             {
8556                 PREAD_DVD_STRUCTURES_HEADER rawKey = OutputBuffer;
8557                 PDVD_COPY_PROTECT_KEY       outputKey = OutputBuffer;
8558 
8559                 // save input parameters
8560                 RtlCopyMemory(keyHeader,
8561                               InputBuffer,
8562                               sizeof(DVD_COPY_PROTECT_KEY));
8563 
8564                 readStructureRequest->Format = DvdDiskKeyDescriptor;
8565                 readStructureRequest->BlockByteOffset.QuadPart = 0;
8566                 readStructureRequest->LayerNumber = 0;
8567                 readStructureRequest->SessionId = keyHeader->SessionId;
8568 
8569                 status = ReadDvdStructure(DeviceExtension,
8570                                           OriginalRequest,
8571                                           InputBuffer,
8572                                           sizeof(DVD_READ_STRUCTURE),
8573                                           OutputBuffer,
8574                                           sizeof(READ_DVD_STRUCTURES_HEADER) + sizeof(DVD_DISK_KEY_DESCRIPTOR),
8575                                           DataLength);
8576 
8577                 // fill the output buffer, it's not touched in DeviceHandleReadDvdStructure()
8578                 // for this specific request type: DvdDiskKeyDescriptor
8579                 if (NT_SUCCESS(status))
8580                 {
8581                     // Shift the data down to its new position.
8582                     RtlMoveMemory(outputKey->KeyData,
8583                                   rawKey->Data,
8584                                   sizeof(DVD_DISK_KEY_DESCRIPTOR));
8585 
8586                     RtlCopyMemory(outputKey,
8587                                   keyHeader,
8588                                   sizeof(DVD_COPY_PROTECT_KEY));
8589 
8590                     outputKey->KeyLength = DVD_DISK_KEY_LENGTH;
8591 
8592                     *DataLength = DVD_DISK_KEY_LENGTH;
8593                 }
8594                 else
8595                 {
8596                     TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
8597                                 "StartSessionReadKey Failed with status %x, %xI64 (%x) bytes\n",
8598                                 status,
8599                                 (unsigned int)*DataLength,
8600                                 ((rawKey->Length[0] << 16) | rawKey->Length[1]) ));
8601                 }
8602 
8603                 FREE_POOL(keyHeader);
8604             }
8605 
8606             // special process finished. return from here.
8607             return status;
8608         }
8609 
8610         if (NT_SUCCESS(status))
8611         {
8612             status = RtlULongSub((ULONG)OutputBufferLength,
8613                                  (ULONG)sizeof(DVD_COPY_PROTECT_KEY), &result);
8614         }
8615 
8616         if (NT_SUCCESS(status))
8617         {
8618             status = RtlULongAdd(result, sizeof(CDVD_KEY_HEADER), &keyLength);
8619         }
8620 
8621         if (NT_SUCCESS(status))
8622         {
8623             //The data length field of REPORT KEY Command occupies two bytes
8624             keyLength = min(keyLength, MAXUSHORT);
8625         }
8626     }
8627     else    //IOCTL_DVD_START_SESSION
8628     {
8629         keyParameters = NULL;
8630         keyLength = sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA);
8631         status = STATUS_SUCCESS;
8632     }
8633 
8634     if (NT_SUCCESS(status))
8635     {
8636         CDB cdb;
8637 
8638         allocationLength = keyLength;
8639 
8640         // Defensive coding. Prefix cannot recognize this usage.
8641         UNREFERENCED_PARAMETER(allocationLength);
8642 
8643         ScratchBuffer_BeginUse(DeviceExtension);
8644 
8645         RtlZeroMemory(&cdb, sizeof(CDB));
8646         // Set up the CDB
8647         cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
8648         cdb.REPORT_KEY.AllocationLength[0] = fourByte->Byte1;
8649         cdb.REPORT_KEY.AllocationLength[1] = fourByte->Byte0;
8650 
8651         // set the specific parameters....
8652         if(IoControlCode == IOCTL_DVD_READ_KEY)
8653         {
8654             if(keyParameters->KeyType == DvdTitleKey)
8655             {
8656                 ULONG logicalBlockAddress;
8657 
8658                 logicalBlockAddress = (ULONG)(keyParameters->Parameters.TitleOffset.QuadPart >>
8659                                               DeviceExtension->SectorShift);
8660 
8661                 fourByte = (PFOUR_BYTE)&(logicalBlockAddress);
8662 
8663                 cdb.REPORT_KEY.LogicalBlockAddress[0] = fourByte->Byte3;
8664                 cdb.REPORT_KEY.LogicalBlockAddress[1] = fourByte->Byte2;
8665                 cdb.REPORT_KEY.LogicalBlockAddress[2] = fourByte->Byte1;
8666                 cdb.REPORT_KEY.LogicalBlockAddress[3] = fourByte->Byte0;
8667             }
8668 
8669             cdb.REPORT_KEY.KeyFormat = (UCHAR)keyParameters->KeyType;
8670             cdb.REPORT_KEY.AGID = (UCHAR)keyParameters->SessionId;
8671             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
8672                         "DvdStartSessionReadKey => sending irp %p (%s)\n",
8673                         OriginalRequest, "READ_KEY"));
8674         }
8675         else
8676         {
8677             cdb.REPORT_KEY.KeyFormat = DVD_REPORT_AGID;
8678             cdb.REPORT_KEY.AGID = 0;
8679             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
8680                         "DvdStartSessionReadKey => sending irp %p (%s)\n",
8681                         OriginalRequest, "START_SESSION"));
8682         }
8683 
8684         status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, keyLength, TRUE, &cdb, 12);
8685 
8686         if (NT_SUCCESS(status))
8687         {
8688             if(IoControlCode == IOCTL_DVD_READ_KEY)
8689             {
8690                 NTSTATUS                tempStatus;
8691                 PDVD_COPY_PROTECT_KEY   copyProtectKey = (PDVD_COPY_PROTECT_KEY)OutputBuffer;
8692                 PCDVD_KEY_HEADER        keyHeader = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;
8693                 ULONG                   dataLength;
8694                 ULONG                   transferLength;
8695 
8696                 tempStatus = RtlULongSub(DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength,
8697                                          FIELD_OFFSET(CDVD_KEY_HEADER, Data),
8698                                          &transferLength);
8699 
8700                 dataLength = (keyHeader->DataLength[0] << 8) + keyHeader->DataLength[1];
8701 
8702                 if (NT_SUCCESS(tempStatus) && (dataLength >= 2))
8703                 {
8704                     // Adjust the data length to ignore the two reserved bytes in the
8705                     // header.
8706                     dataLength -= 2;
8707 
8708                     // take the minimum of the transferred length and the
8709                     // length as specified in the header.
8710                     if(dataLength < transferLength)
8711                     {
8712                         transferLength = dataLength;
8713                     }
8714 
8715                     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
8716                                 "DvdDeviceControlCompletion: [%p] - READ_KEY with "
8717                                 "transfer length of (%d or %d) bytes\n",
8718                                 OriginalRequest,
8719                                 dataLength,
8720                                 DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength - 2));
8721 
8722                     // Copy the key data into the return buffer
8723                     if(copyProtectKey->KeyType == DvdTitleKey)
8724                     {
8725                         RtlMoveMemory(copyProtectKey->KeyData,
8726                                       keyHeader->Data + 1,
8727                                       transferLength - 1);
8728 
8729                         copyProtectKey->KeyData[transferLength - 1] = 0;
8730 
8731                         // If this is a title key then we need to copy the CGMS flags
8732                         // as well.
8733                         copyProtectKey->KeyFlags = *(keyHeader->Data);
8734 
8735                     }
8736                     else
8737                     {
8738                         RtlMoveMemory(copyProtectKey->KeyData,
8739                                       keyHeader->Data,
8740                                       transferLength);
8741                     }
8742 
8743                     copyProtectKey->KeyLength = sizeof(DVD_COPY_PROTECT_KEY);
8744                     copyProtectKey->KeyLength += transferLength;
8745 
8746                     *DataLength = copyProtectKey->KeyLength;
8747                 }
8748                 else
8749                 {
8750                     //There is no valid data from drive.
8751                     //This may happen when Key Format = 0x3f that does not require data back from drive.
8752                     status = STATUS_SUCCESS;
8753                     *DataLength = 0;
8754                 }
8755             }
8756             else
8757             {
8758                 PDVD_SESSION_ID         sessionId = (PDVD_SESSION_ID)OutputBuffer;
8759                 PCDVD_KEY_HEADER        keyHeader = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer;
8760                 PCDVD_REPORT_AGID_DATA  keyData = (PCDVD_REPORT_AGID_DATA)keyHeader->Data;
8761 
8762                 *sessionId = keyData->AGID;
8763 
8764                 *DataLength = sizeof(DVD_SESSION_ID);
8765             }
8766         }
8767 
8768         // nothing to do after the command finishes.
8769         ScratchBuffer_EndUse(DeviceExtension);
8770     }
8771 
8772     return status;
8773 }
8774 
8775 
_IRQL_requires_max_(APC_LEVEL)8776 _IRQL_requires_max_(APC_LEVEL)
8777 NTSTATUS
8778 DeviceHandleDvdSendKey(
8779     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
8780     _In_  WDFREQUEST               Request,
8781     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
8782     _Out_ size_t *                 DataLength
8783     )
8784 /*++
8785 
8786 Routine Description:
8787 
8788    Handle request of IOCTL_DVD_SEND_KEY
8789                      IOCTL_DVD_SEND_KEY2
8790 
8791 Arguments:
8792 
8793     DeviceExtension - device context
8794     Request - request to be handled
8795     RequestParameters - request parameter
8796     DataLength - transferred data length
8797 
8798 Return Value:
8799 
8800     NTSTATUS
8801 
8802 --*/
8803 {
8804     NTSTATUS                status = STATUS_SUCCESS;
8805     PVOID                   inputBuffer = NULL;
8806 
8807     PAGED_CODE ();
8808 
8809     *DataLength = 0;
8810 
8811     status = WdfRequestRetrieveInputBuffer(Request,
8812                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8813                                            &inputBuffer,
8814                                            NULL);
8815 
8816     if (NT_SUCCESS(status))
8817     {
8818         status = DvdSendKey(DeviceExtension,
8819                             Request,
8820                             inputBuffer,
8821                             RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8822                             DataLength);
8823     }
8824 
8825     return status;
8826 }
8827 
_IRQL_requires_max_(APC_LEVEL)8828 _IRQL_requires_max_(APC_LEVEL)
8829 NTSTATUS
8830 DvdSendKey(
8831     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
8832     _In_opt_  WDFREQUEST           OriginalRequest,
8833     _In_  PVOID                    InputBuffer,
8834     _In_  size_t                   InputBufferLength,
8835     _Out_ size_t *                 DataLength
8836     )
8837 /*++
8838 
8839 Routine Description:
8840 
8841    base function to handle request of IOCTL_DVD_SEND_KEY(2)
8842    NOTE: cdrom does not process this IOCTL if the input buffer length is bigger than Port transfer length.
8843 
8844 Arguments:
8845 
8846     DeviceExtension - device context
8847     OriginalRequest - original request to be handled
8848     InputBuffer - input buffer
8849     InputBufferLength - length of input buffer
8850 
8851 Return Value:
8852 
8853     NTSTATUS
8854     DataLength - returned data length
8855 
8856 --*/
8857 {
8858     NTSTATUS                status = STATUS_SUCCESS;
8859     PDVD_COPY_PROTECT_KEY   key = (PDVD_COPY_PROTECT_KEY)InputBuffer;
8860 
8861     ULONG                   keyLength = 0;
8862     ULONG                   result = 0;
8863     PFOUR_BYTE              fourByte;
8864 
8865     PAGED_CODE ();
8866 
8867     UNREFERENCED_PARAMETER(InputBufferLength);
8868 
8869     *DataLength = 0;
8870 
8871     if (NT_SUCCESS(status))
8872     {
8873         if ((key->KeyLength < sizeof(DVD_COPY_PROTECT_KEY)) ||
8874             ((key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)) > DeviceExtension->ScratchContext.ScratchBufferSize))
8875         {
8876             NT_ASSERT(FALSE);
8877             status = STATUS_INTERNAL_ERROR;
8878         }
8879     }
8880 
8881     if (NT_SUCCESS(status))
8882     {
8883         status = RtlULongSub(key->KeyLength, sizeof(DVD_COPY_PROTECT_KEY), &result);
8884     }
8885 
8886     if (NT_SUCCESS(status))
8887     {
8888         status = RtlULongAdd(result, sizeof(CDVD_KEY_HEADER), &keyLength);
8889     }
8890 
8891     if (NT_SUCCESS(status))
8892     {
8893         keyLength = min(keyLength, DeviceExtension->ScratchContext.ScratchBufferSize);
8894 
8895         if (keyLength < 2)
8896         {
8897             status = STATUS_INVALID_PARAMETER;
8898         }
8899     }
8900 
8901     if (NT_SUCCESS(status))
8902     {
8903         PCDVD_KEY_HEADER            keyBuffer = NULL;
8904         CDB                         cdb;
8905 
8906         ScratchBuffer_BeginUse(DeviceExtension);
8907 
8908         // prepare the input buffer
8909         keyBuffer = (PCDVD_KEY_HEADER)DeviceExtension->ScratchContext.ScratchBuffer;
8910 
8911         // keylength is decremented here by two because the
8912         // datalength does not include the header, which is two
8913         // bytes.  keylength is immediately incremented later
8914         // by the same amount.
8915         keyLength -= 2;
8916         fourByte = (PFOUR_BYTE)&keyLength;
8917         keyBuffer->DataLength[0] = fourByte->Byte1;
8918         keyBuffer->DataLength[1] = fourByte->Byte0;
8919         keyLength += 2;
8920 
8921         RtlMoveMemory(keyBuffer->Data,
8922                       key->KeyData,
8923                       key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY));
8924 
8925         RtlZeroMemory(&cdb, sizeof(CDB));
8926         // Set up the CDB
8927         cdb.REPORT_KEY.OperationCode = SCSIOP_SEND_KEY;
8928 
8929         cdb.SEND_KEY.ParameterListLength[0] = fourByte->Byte1;
8930         cdb.SEND_KEY.ParameterListLength[1] = fourByte->Byte0;
8931         cdb.SEND_KEY.KeyFormat = (UCHAR)key->KeyType;
8932         cdb.SEND_KEY.AGID = (UCHAR)key->SessionId;
8933 
8934         status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, keyLength, FALSE, &cdb, 12);
8935 
8936         // nothing to do after the command finishes.
8937         ScratchBuffer_EndUse(DeviceExtension);
8938     }
8939 
8940     return status;
8941 }
8942 
8943 
_IRQL_requires_max_(APC_LEVEL)8944 _IRQL_requires_max_(APC_LEVEL)
8945 NTSTATUS
8946 DeviceHandleSetReadAhead(
8947     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
8948     _In_  WDFREQUEST               Request,
8949     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
8950     _Out_ size_t *                 DataLength
8951     )
8952 /*++
8953 
8954 Routine Description:
8955 
8956    Handle request of IOCTL_STORAGE_SET_READ_AHEAD
8957 
8958 Arguments:
8959 
8960     DeviceExtension - device context
8961     Request - request to be handled
8962     RequestParameters - request parameter
8963     DataLength - transferred data length
8964 
8965 Return Value:
8966 
8967     NTSTATUS
8968 
8969 --*/
8970 {
8971     NTSTATUS                status = STATUS_SUCCESS;
8972     PSTORAGE_SET_READ_AHEAD readAhead = NULL;
8973 
8974     PAGED_CODE ();
8975 
8976     *DataLength = 0;
8977 
8978     status = WdfRequestRetrieveInputBuffer(Request,
8979                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
8980                                            (PVOID*)&readAhead,
8981                                            NULL);
8982 
8983     if (NT_SUCCESS(status))
8984     {
8985         ULONG       transferSize = 0;
8986         CDB         cdb;
8987         ULONG       blockAddress;
8988         PFOUR_BYTE  fourByte = (PFOUR_BYTE)&blockAddress;
8989 
8990         ScratchBuffer_BeginUse(DeviceExtension);
8991 
8992         RtlZeroMemory(&cdb, sizeof(CDB));
8993         // Set up the CDB
8994         cdb.SET_READ_AHEAD.OperationCode = SCSIOP_SET_READ_AHEAD;
8995 
8996         blockAddress = (ULONG)(readAhead->TriggerAddress.QuadPart >>
8997                                DeviceExtension->SectorShift);
8998 
8999         // Defensive coding. Prefix cannot recognize this usage.
9000         UNREFERENCED_PARAMETER(blockAddress);
9001 
9002         cdb.SET_READ_AHEAD.TriggerLBA[0] = fourByte->Byte3;
9003         cdb.SET_READ_AHEAD.TriggerLBA[1] = fourByte->Byte2;
9004         cdb.SET_READ_AHEAD.TriggerLBA[2] = fourByte->Byte1;
9005         cdb.SET_READ_AHEAD.TriggerLBA[3] = fourByte->Byte0;
9006 
9007         blockAddress = (ULONG)(readAhead->TargetAddress.QuadPart >>
9008                                DeviceExtension->SectorShift);
9009 
9010         cdb.SET_READ_AHEAD.ReadAheadLBA[0] = fourByte->Byte3;
9011         cdb.SET_READ_AHEAD.ReadAheadLBA[1] = fourByte->Byte2;
9012         cdb.SET_READ_AHEAD.ReadAheadLBA[2] = fourByte->Byte1;
9013         cdb.SET_READ_AHEAD.ReadAheadLBA[3] = fourByte->Byte0;
9014 
9015         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);
9016 
9017         if (NT_SUCCESS(status))
9018         {
9019             *DataLength = 0;
9020         }
9021 
9022         // nothing to do after the command finishes.
9023         ScratchBuffer_EndUse(DeviceExtension);
9024     }
9025 
9026     return status;
9027 }
9028 
_IRQL_requires_max_(APC_LEVEL)9029 _IRQL_requires_max_(APC_LEVEL)
9030 NTSTATUS
9031 DeviceHandleSetSpeed(
9032     _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension,
9033     _In_  WDFREQUEST               Request,
9034     _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
9035     _Out_ size_t *                 DataLength
9036     )
9037 /*++
9038 
9039 Routine Description:
9040 
9041    Handle request of IOCTL_CDROM_SET_SPEED
9042 
9043 Arguments:
9044 
9045     DeviceExtension - device context
9046     Request - request to be handled
9047     RequestParameters - request parameter
9048     DataLength - transferred data length
9049 
9050 Return Value:
9051 
9052     NTSTATUS
9053 
9054 --*/
9055 {
9056     NTSTATUS            status = STATUS_SUCCESS;
9057     PCDROM_DATA         cdData = &(DeviceExtension->DeviceAdditionalData);
9058     PCDROM_SET_SPEED    inputBuffer = NULL;
9059 
9060     PAGED_CODE ();
9061 
9062     *DataLength = 0;
9063 
9064     status = WdfRequestRetrieveInputBuffer(Request,
9065                                            RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
9066                                            (PVOID*)&inputBuffer,
9067                                            NULL);
9068 
9069     if (NT_SUCCESS(status))
9070     {
9071         ULONG               transferSize = 0;
9072         CDB                 cdb;
9073         CDROM_SPEED_REQUEST requestType = inputBuffer->RequestType;
9074 
9075         ScratchBuffer_BeginUse(DeviceExtension);
9076 
9077         RtlZeroMemory(&cdb, sizeof(CDB));
9078         // Set up the CDB
9079         if (requestType == CdromSetSpeed)
9080         {
9081             PCDROM_SET_SPEED speed = inputBuffer;
9082 
9083             cdb.SET_CD_SPEED.OperationCode = SCSIOP_SET_CD_SPEED;
9084             cdb.SET_CD_SPEED.RotationControl = speed->RotationControl;
9085             REVERSE_BYTES_SHORT(&cdb.SET_CD_SPEED.ReadSpeed, &speed->ReadSpeed);
9086             REVERSE_BYTES_SHORT(&cdb.SET_CD_SPEED.WriteSpeed, &speed->WriteSpeed);
9087         }
9088         else
9089         {
9090             PCDROM_SET_STREAMING    stream = (PCDROM_SET_STREAMING)inputBuffer;
9091             PPERFORMANCE_DESCRIPTOR perfDescriptor;
9092 
9093             transferSize = sizeof(PERFORMANCE_DESCRIPTOR);
9094 
9095             perfDescriptor = DeviceExtension->ScratchContext.ScratchBuffer;
9096             RtlZeroMemory(perfDescriptor, transferSize);
9097 
9098             perfDescriptor->RandomAccess = stream->RandomAccess;
9099             perfDescriptor->Exact = stream->SetExact;
9100             perfDescriptor->RestoreDefaults = stream->RestoreDefaults;
9101             perfDescriptor->WriteRotationControl = stream->RotationControl;
9102 
9103             REVERSE_BYTES(&perfDescriptor->StartLba,  &stream->StartLba);
9104             REVERSE_BYTES(&perfDescriptor->EndLba,    &stream->EndLba);
9105             REVERSE_BYTES(&perfDescriptor->ReadSize,  &stream->ReadSize);
9106             REVERSE_BYTES(&perfDescriptor->ReadTime,  &stream->ReadTime);
9107             REVERSE_BYTES(&perfDescriptor->WriteSize, &stream->WriteSize);
9108             REVERSE_BYTES(&perfDescriptor->WriteTime, &stream->WriteTime);
9109 
9110             cdb.SET_STREAMING.OperationCode = SCSIOP_SET_STREAMING;
9111             REVERSE_BYTES_SHORT(&cdb.SET_STREAMING.ParameterListLength, &transferSize);
9112 
9113             // set value in extension by user inputs.
9114             cdData->RestoreDefaults = stream->Persistent ? FALSE : TRUE;
9115 
9116             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceHandleSetSpeed: Restore default speed on media change set to %s\n",
9117                        cdData->RestoreDefaults ? "true" : "false"));
9118         }
9119 
9120         status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);
9121 
9122         if (NT_SUCCESS(status))
9123         {
9124             *DataLength = 0;
9125         }
9126 
9127         // nothing to do after the command finishes.
9128         ScratchBuffer_EndUse(DeviceExtension);
9129     }
9130 
9131     return status;
9132 }
9133 
9134 
9135