/*-- Copyright (C) Microsoft Corporation. All rights reserved. Module Name: ioctl.c Abstract: Include all funtions for processing IOCTLs Environment: kernel mode only Notes: Revision History: --*/ #include "stddef.h" #include "string.h" #include "ntddk.h" #include "ntddstor.h" #include "cdrom.h" #include "ioctl.h" #include "scratch.h" #include "mmc.h" #ifdef DEBUG_USE_WPP #include "ioctl.tmh" #endif #define FirstDriveLetter 'C' #define LastDriveLetter 'Z' #if DBG LPCSTR READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor+1] = { "Physical", "Copyright", "DiskKey", "BCA", "Manufacturer", "Unknown" }; #endif // DBG _IRQL_requires_max_(APC_LEVEL) VOID GetConfigurationDataConversionTypeAllToTypeOne( _In_ FEATURE_NUMBER RequestedFeature, _In_ PSCSI_REQUEST_BLOCK Srb, _Out_ size_t * DataLength ); _IRQL_requires_max_(APC_LEVEL) VOID GetConfigurationDataSynthesize( _In_reads_bytes_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize, _Out_writes_bytes_(OutputBufferSize) PVOID OutputBuffer, _In_ size_t OutputBufferSize, _In_ FEATURE_NUMBER StartingFeature, _In_ ULONG RequestType, _Out_ size_t * DataLength ); _IRQL_requires_max_(APC_LEVEL) PCDB RequestGetScsiPassThroughCdb( _In_ PIRP Irp ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, DeviceIsPlayActive) #pragma alloc_text(PAGE, RequestHandleGetDvdRegion) #pragma alloc_text(PAGE, RequestHandleReadTOC) #pragma alloc_text(PAGE, RequestHandleReadTocEx) #pragma alloc_text(PAGE, RequestHandleGetConfiguration) #pragma alloc_text(PAGE, RequestHandleGetDriveGeometry) #pragma alloc_text(PAGE, RequestHandleDiskVerify) #pragma alloc_text(PAGE, RequestHandleCheckVerify) #pragma alloc_text(PAGE, RequestHandleFakePartitionInfo) #pragma alloc_text(PAGE, RequestHandleEjectionControl) #pragma alloc_text(PAGE, RequestHandleEnableStreaming) #pragma alloc_text(PAGE, RequestHandleSendOpcInformation) #pragma alloc_text(PAGE, RequestHandleGetPerformance) #pragma alloc_text(PAGE, RequestHandleMcnSyncFakeIoctl) #pragma alloc_text(PAGE, RequestHandleLoadEjectMedia) #pragma alloc_text(PAGE, RequestHandleReserveRelease) #pragma alloc_text(PAGE, RequestHandlePersistentReserve) #pragma alloc_text(PAGE, DeviceHandleRawRead) #pragma alloc_text(PAGE, DeviceHandlePlayAudioMsf) #pragma alloc_text(PAGE, DeviceHandleReadQChannel) #pragma alloc_text(PAGE, ReadQChannel) #pragma alloc_text(PAGE, DeviceHandlePauseAudio) #pragma alloc_text(PAGE, DeviceHandleResumeAudio) #pragma alloc_text(PAGE, DeviceHandleSeekAudioMsf) #pragma alloc_text(PAGE, DeviceHandleStopAudio) #pragma alloc_text(PAGE, DeviceHandleGetSetVolume) #pragma alloc_text(PAGE, DeviceHandleReadDvdStructure) #pragma alloc_text(PAGE, ReadDvdStructure) #pragma alloc_text(PAGE, DeviceHandleDvdEndSession) #pragma alloc_text(PAGE, DeviceHandleDvdStartSessionReadKey) #pragma alloc_text(PAGE, DvdStartSessionReadKey) #pragma alloc_text(PAGE, DeviceHandleDvdSendKey) #pragma alloc_text(PAGE, DvdSendKey) #pragma alloc_text(PAGE, DeviceHandleSetReadAhead) #pragma alloc_text(PAGE, DeviceHandleSetSpeed) #pragma alloc_text(PAGE, RequestHandleExclusiveAccessQueryLockState) #pragma alloc_text(PAGE, RequestHandleExclusiveAccessLockDevice) #pragma alloc_text(PAGE, RequestHandleExclusiveAccessUnlockDevice) #pragma alloc_text(PAGE, RequestHandleScsiPassThrough) #pragma alloc_text(PAGE, RequestGetScsiPassThroughCdb) #pragma alloc_text(PAGE, GetConfigurationDataConversionTypeAllToTypeOne) #pragma alloc_text(PAGE, GetConfigurationDataSynthesize) #endif NTSTATUS RequestHandleUnknownIoctl( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request ) /*++ Routine Description: All unknown IOCTLs will be forward to lower level driver. Arguments: Device - device object Request - request to be handled Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request); BOOLEAN syncRequired = requestContext->SyncRequired; ULONG sendOptionsFlags = 0; BOOLEAN requestSent = FALSE; WdfRequestFormatRequestUsingCurrentType(Request); if (syncRequired) { sendOptionsFlags = WDF_REQUEST_SEND_OPTION_SYNCHRONOUS; } else { WdfRequestSetCompletionRoutine(Request, RequestDummyCompletionRoutine, NULL); } status = RequestSend(deviceExtension, Request, deviceExtension->IoTarget, sendOptionsFlags, &requestSent); if (requestSent) { if (syncRequired) { // the request needs to be completed here. RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request)); } } else { // failed to send the request to IoTarget RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request)); } return status; } _IRQL_requires_max_(APC_LEVEL) BOOLEAN DeviceIsPlayActive( _In_ WDFDEVICE Device ) /*++ Routine Description: This routine determines if the cd is currently playing music. Arguments: Device - Device object. Return Value: BOOLEAN - TRUE if the device is playing music. --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); PSUB_Q_CURRENT_POSITION currentBuffer; size_t bytesRead = 0; PAGED_CODE (); // if we don't think it is playing audio, don't bother checking. if (!deviceExtension->DeviceAdditionalData.PlayActive) { return FALSE; } // Allocate the required memory NT_ASSERT(sizeof(SUB_Q_CURRENT_POSITION) >= sizeof(CDROM_SUB_Q_DATA_FORMAT)); currentBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, sizeof(SUB_Q_CURRENT_POSITION), CDROM_TAG_PLAY_ACTIVE); if (currentBuffer == NULL) { return FALSE; } // set the options in the output buffer format ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION; ((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0; // Send SCSI command to read Q Channel information. status = ReadQChannel(deviceExtension, NULL, currentBuffer, sizeof(CDROM_SUB_Q_DATA_FORMAT), currentBuffer, sizeof(SUB_Q_CURRENT_POSITION), &bytesRead); if (!NT_SUCCESS(status)) { ExFreePool(currentBuffer); return FALSE; } // update the playactive flag appropriately if (currentBuffer->Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) { deviceExtension->DeviceAdditionalData.PlayActive = TRUE; } else { deviceExtension->DeviceAdditionalData.PlayActive = FALSE; } ExFreePool(currentBuffer); return deviceExtension->DeviceAdditionalData.PlayActive; } NTSTATUS RequestHandleGetInquiryData( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength) /*++ Routine Description: Handler for IOCTL_CDROM_GET_INQUIRY_DATA Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0) { status = STATUS_BUFFER_TOO_SMALL; } else { PVOID outputBuffer = NULL; *DataLength = min(cdData->CachedInquiryDataByteCount, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength); status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); if (NT_SUCCESS(status) && (outputBuffer != NULL)) { // Always copy as much data as possible RtlCopyMemory(outputBuffer, cdData->CachedInquiryData, *DataLength); } // and finally decide between two possible status values if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < cdData->CachedInquiryDataByteCount) { status = STATUS_BUFFER_OVERFLOW; } } return status; } NTSTATUS RequestHandleGetMediaTypeEx( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _Out_ size_t * DataLength ) /*++ Routine Description: Handler for IOCTL_STORAGE_GET_MEDIA_TYPES_EX Arguments: DeviceExtension - device context Request - request to be handled DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PGET_MEDIA_TYPES mediaTypes = NULL; PDEVICE_MEDIA_INFO mediaInfo = NULL; //&mediaTypes->MediaInfo[0]; ULONG sizeNeeded = 0; PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo; *DataLength = 0; // Must run below dispatch level. if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { NT_ASSERT(FALSE); return STATUS_INVALID_LEVEL; } sizeNeeded = sizeof(GET_MEDIA_TYPES); // IsMmc is static... if (cdData->Mmc.IsMmc) { sizeNeeded += sizeof(DEVICE_MEDIA_INFO) * 1; // return two media types } status = WdfRequestRetrieveOutputBuffer(Request, sizeNeeded, (PVOID*)&mediaTypes, NULL); if (NT_SUCCESS(status) && (mediaTypes != NULL)) { mediaInfo = &mediaTypes->MediaInfo[0]; RtlZeroMemory(mediaTypes, sizeNeeded); // ISSUE-2000/5/11-henrygab - need to update GET_MEDIA_TYPES_EX mediaTypes->DeviceType = cdData->DriveDeviceType; mediaTypes->MediaInfoCount = 1; mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = CD_ROM; mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_ONLY; mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = DeviceExtension->DiskGeometry.Cylinders.QuadPart; mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = DeviceExtension->DiskGeometry.TracksPerCylinder; mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = DeviceExtension->DiskGeometry.SectorsPerTrack; mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = DeviceExtension->DiskGeometry.BytesPerSector; if (cdData->Mmc.IsMmc) { // also report a removable disk mediaTypes->MediaInfoCount += 1; mediaInfo++; mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia; mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE; mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = DeviceExtension->DiskGeometry.Cylinders.QuadPart; mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = DeviceExtension->DiskGeometry.TracksPerCylinder; mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = DeviceExtension->DiskGeometry.SectorsPerTrack; mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = DeviceExtension->DiskGeometry.BytesPerSector; mediaInfo--; } // Status will either be success, if media is present, or no media. // It would be optimal to base from density code and medium type, but not all devices // have values for these fields. // Send a TUR to determine if media is present, only if the device is not in ZPODD mode. if ((!EXCLUSIVE_MODE(cdData) || EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request))) && ((zpoddInfo == NULL) || (zpoddInfo->InZeroPowerState == FALSE))) { SCSI_REQUEST_BLOCK srb; PCDB cdb = (PCDB)srb.Cdb; RtlZeroMemory(&srb,sizeof(SCSI_REQUEST_BLOCK)); srb.CdbLength = 6; cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; srb.TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT; status = DeviceSendSrbSynchronously(DeviceExtension->Device, &srb, NULL, 0, FALSE, Request); if (NT_SUCCESS(status)) { // set the disk's media as current if we can write to it. if (cdData->Mmc.IsMmc && cdData->Mmc.WriteAllowed) { mediaInfo++; SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED); mediaInfo--; } else { SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED); } } else { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleGetMediaTypeEx: GET_MEDIA_TYPES status of TUR - %lx\n", status)); } } // per legacy cdrom behavior, always return success status = STATUS_SUCCESS; } *DataLength = sizeNeeded; return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleGetDvdRegion( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _Out_ size_t * DataLength ) /*++ Routine Description: Handler for IOCTL_DVD_GET_REGION Arguments: DeviceExtension - device context Request - request to be handled DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PVOID outputBuffer = NULL; size_t bytesReturned = 0; PDVD_COPY_PROTECT_KEY copyProtectKey = NULL; ULONG keyLength = 0; PDVD_DESCRIPTOR_HEADER dvdHeader; PDVD_COPYRIGHT_DESCRIPTOR copyRightDescriptor; PDVD_REGION dvdRegion = NULL; PDVD_READ_STRUCTURE readStructure = NULL; PDVD_RPC_KEY rpcKey; PAGED_CODE (); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "RequestHandleGetDvdRegion: [%p] IOCTL_DVD_GET_REGION\n", Request)); *DataLength = 0; // reject the request if it's not a DVD device. if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD) { status = STATUS_INVALID_DEVICE_REQUEST; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, sizeof(DVD_REGION), &outputBuffer, NULL); } if (NT_SUCCESS(status)) { // figure out how much data buffer we need keyLength = max((sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR)), sizeof(DVD_READ_STRUCTURE)); keyLength = max(keyLength, DVD_RPC_KEY_LENGTH); // round the size to nearest ULONGLONG -- why? // could this be to deal with device alignment issues? keyLength += sizeof(ULONGLONG) - (keyLength & (sizeof(ULONGLONG) - 1)); readStructure = ExAllocatePoolWithTag(NonPagedPoolNx, keyLength, DVD_TAG_READ_KEY); if (readStructure == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } } if (NT_SUCCESS(status)) { RtlZeroMemory (readStructure, keyLength); readStructure->Format = DvdCopyrightDescriptor; // use READ_STRUCTURE to read copyright descriptor status = ReadDvdStructure(DeviceExtension, Request, readStructure, keyLength, readStructure, sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR), &bytesReturned); } if (NT_SUCCESS(status)) { // we got the copyright descriptor, so now get the region if possible dvdHeader = (PDVD_DESCRIPTOR_HEADER) readStructure; copyRightDescriptor = (PDVD_COPYRIGHT_DESCRIPTOR) dvdHeader->Data; // the original irp's systembuffer has a copy of the info that // should be passed down in the request dvdRegion = outputBuffer; dvdRegion->CopySystem = copyRightDescriptor->CopyrightProtectionType; dvdRegion->RegionData = copyRightDescriptor->RegionManagementInformation; // now reuse the buffer to request the copy protection info copyProtectKey = (PDVD_COPY_PROTECT_KEY) readStructure; RtlZeroMemory (copyProtectKey, DVD_RPC_KEY_LENGTH); copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; copyProtectKey->KeyType = DvdGetRpcKey; // send a request for READ_KEY status = DvdStartSessionReadKey(DeviceExtension, IOCTL_DVD_READ_KEY, Request, copyProtectKey, DVD_RPC_KEY_LENGTH, copyProtectKey, DVD_RPC_KEY_LENGTH, &bytesReturned); } if (NT_SUCCESS(status)) { // the request succeeded. if a supported scheme is returned, // then return the information to the caller rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData; if (rpcKey->RpcScheme == 1) { if (rpcKey->TypeCode) { dvdRegion->SystemRegion = ~rpcKey->RegionMask; dvdRegion->ResetCount = rpcKey->UserResetsAvailable; } else { // the drive has not been set for any region dvdRegion->SystemRegion = 0; dvdRegion->ResetCount = rpcKey->UserResetsAvailable; } *DataLength = sizeof(DVD_REGION); } else { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestHandleGetDvdRegion => rpcKey->RpcScheme != 1\n")); status = STATUS_INVALID_DEVICE_REQUEST; } } // Clean up if (readStructure != NULL) { ExFreePool(readStructure); } return status; } NTSTATUS RequestValidateRawRead( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_CDROM_RAW_READ Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PVOID inputBuffer = NULL; PIRP irp = NULL; PIO_STACK_LOCATION currentStack = NULL; LARGE_INTEGER startingOffset = {0}; ULONGLONG transferBytes = 0; ULONGLONG endOffset; ULONGLONG mdlBytes; RAW_READ_INFO rawReadInfo = {0}; *DataLength = 0; irp = WdfRequestWdmGetIrp(Request); currentStack = IoGetCurrentIrpStackLocation(irp); status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); // Check that ending sector is on disc and buffers are there and of // correct size. if (NT_SUCCESS(status) && (RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer == NULL)) { // This is a call from user space. This is the only time that we need to validate parameters. // Validate the input and get the input buffer into Type3InputBuffer // so the rest of the code will be uniform. if (inputBuffer != NULL) { currentStack->Parameters.DeviceIoControl.Type3InputBuffer = inputBuffer; RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer = inputBuffer; if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(RAW_READ_INFO)) { *DataLength = sizeof(RAW_READ_INFO); status = STATUS_BUFFER_TOO_SMALL; } } else { status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { // Since this ioctl is METHOD_OUT_DIRECT, we need to copy away the input buffer before interpreting it. // This prevents a malicious app from messing with the input buffer while we are interpreting it. rawReadInfo = *(PRAW_READ_INFO)RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer; startingOffset.QuadPart = rawReadInfo.DiskOffset.QuadPart; if ((rawReadInfo.TrackMode == CDDA) || (rawReadInfo.TrackMode == YellowMode2) || (rawReadInfo.TrackMode == XAForm2) ) { transferBytes = (ULONGLONG)rawReadInfo.SectorCount * RAW_SECTOR_SIZE; } else if (rawReadInfo.TrackMode == RawWithSubCode) { transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_SUBCODE_SIZE; } else if (rawReadInfo.TrackMode == RawWithC2) { transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_SIZE; } else if (rawReadInfo.TrackMode == RawWithC2AndSubCode) { transferBytes = (ULONGLONG)rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE; } else { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: Invalid TrackMode type %x for XA read\n", rawReadInfo.TrackMode )); } endOffset = (ULONGLONG)rawReadInfo.SectorCount * COOKED_SECTOR_SIZE; endOffset += startingOffset.QuadPart; // check for overflows.... if (rawReadInfo.SectorCount == 0) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: Invalid I/O parameters for XA " "Read (zero sectors requested)\n")); status = STATUS_INVALID_PARAMETER; } else if (transferBytes < (ULONGLONG)(rawReadInfo.SectorCount)) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: Invalid I/O parameters for XA " "Read (TransferBytes Overflow)\n")); status = STATUS_INVALID_PARAMETER; } else if (endOffset < (ULONGLONG)startingOffset.QuadPart) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: Invalid I/O parameters for XA " "Read (EndingOffset Overflow)\n")); status = STATUS_INVALID_PARAMETER; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < transferBytes) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: Invalid I/O parameters for XA " "Read (Bad buffer size)\n")); status = STATUS_INVALID_PARAMETER; } else if (endOffset > (ULONGLONG)DeviceExtension->PartitionLength.QuadPart) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: Invalid I/O parameters for XA " "Read (Request Out of Bounds)\n")); status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { // cannot validate the MdlAddress, since it is not included in any // other location per the DDK and file system calls. // validate the mdl describes at least the number of bytes // requested from us. mdlBytes = (ULONGLONG)MmGetMdlByteCount(irp->MdlAddress); if (mdlBytes < transferBytes) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: Invalid MDL %s, Irp %p\n", "size (5)", irp)); status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { // check the buffer for alignment // This is important for x86 as some busses (ie ATAPI) // require word-aligned buffers. if ( ((ULONG_PTR)MmGetMdlVirtualAddress(irp->MdlAddress)) & DeviceExtension->AdapterDescriptor->AlignmentMask ) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: Invalid I/O parameters for " "XA Read (Buffer %p not aligned with mask %x\n", RequestParameters.Parameters.DeviceIoControl.Type3InputBuffer, DeviceExtension->AdapterDescriptor->AlignmentMask)); status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { // Validate the request is not too large for the adapter BOOLEAN bufferIsPageAligned = FALSE; ULONG maxLength = 0; // if buffer is not page-aligned, then subtract one as the // transfer could cross a page boundary. if ((((ULONG_PTR)MmGetMdlVirtualAddress(irp->MdlAddress)) & (PAGE_SIZE-1)) == 0) { bufferIsPageAligned = TRUE; } if (bufferIsPageAligned) { maxLength = cdData->MaxPageAlignedTransferBytes; } else { maxLength = cdData->MaxUnalignedTransferBytes; } if (transferBytes > maxLength) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestValidateRawRead: The XA Read (type %x) would require %I64x bytes, " "but the adapter can only handle %x bytes (for a%saligned buffer)\n", rawReadInfo.TrackMode, transferBytes, maxLength, (bufferIsPageAligned ? " " : "n un") )); status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { // // HACKHACK - REF #0001 // The retry count will be in this irp's IRP_MN function, // as the new irp was freed, and we therefore cannot use // this irp's next stack location for this function. // This may be a good location to store this info for // when we remove RAW_READ (mode switching), as we will // no longer have the nextIrpStackLocation to play with // when that occurs // currentStack->MinorFunction = MAXIMUM_RETRIES; // HACKHACK - REF #0001 } return status; } NTSTATUS RequestValidateReadTocEx( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_CDROM_READ_TOC_EX Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_READ_TOC_EX inputBuffer = NULL; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_READ_TOC_EX)) { status = STATUS_INFO_LENGTH_MISMATCH; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE) { status = STATUS_BUFFER_TOO_SMALL; *DataLength = MINIMUM_CDROM_READ_TOC_EX_SIZE; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > ((USHORT)-1)) { status = STATUS_INVALID_PARAMETER; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength & DeviceExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if ((inputBuffer->Reserved1 != 0) || (inputBuffer->Reserved2 != 0) || (inputBuffer->Reserved3 != 0)) { status = STATUS_INVALID_PARAMETER; } // NOTE: when adding new formats, ensure that first two bytes // specify the amount of additional data available. else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_TOC ) || (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_FULL_TOC) || (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_CDTEXT )) { // SessionTrack field is used } else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) || (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_PMA) || (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_ATIP)) { // SessionTrack field is reserved if (inputBuffer->SessionTrack != 0) { status = STATUS_INVALID_PARAMETER; } } else { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateReadToc( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_CDROM_READ_TOC Arguments: DeviceExtension - device context RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC)) { // they didn't request the entire TOC -- use _EX version // for partial transfers and such. status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(CDROM_TOC); } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength & DeviceExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; } return status; } NTSTATUS RequestValidateGetLastSession( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_CDROM_GET_LAST_SESSION Arguments: DeviceExtension - device context RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC_SESSION_DATA)) { status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(CDROM_TOC_SESSION_DATA); } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength & DeviceExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; } return status; } NTSTATUS RequestValidateReadQChannel( _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_CDROM_READ_Q_CHANNEL Arguments: Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_SUB_Q_DATA_FORMAT inputBuffer = NULL; ULONG transferByteCount = 0; *DataLength = 0; if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SUB_Q_DATA_FORMAT)) { status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { // check for all valid types of request if (inputBuffer->Format == IOCTL_CDROM_CURRENT_POSITION) { transferByteCount = sizeof(SUB_Q_CURRENT_POSITION); } else if (inputBuffer->Format == IOCTL_CDROM_MEDIA_CATALOG) { transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER); } else if (inputBuffer->Format == IOCTL_CDROM_TRACK_ISRC) { transferByteCount = sizeof(SUB_Q_TRACK_ISRC); } else { // Format not valid status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < transferByteCount) { status = STATUS_BUFFER_TOO_SMALL; *DataLength = transferByteCount; } } return status; } NTSTATUS RequestValidateDvdReadStructure( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_DVD_READ_STRUCTURE Arguments: DeviceExtension - device context RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; *DataLength = 0; if (NT_SUCCESS(status)) { if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_READ_STRUCTURE)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateDvdReadStructure - input buffer " "length too small (was %d should be %d)\n", (int)RequestParameters.Parameters.DeviceIoControl.InputBufferLength, sizeof(DVD_READ_STRUCTURE))); status = STATUS_INVALID_PARAMETER; } else if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(READ_DVD_STRUCTURES_HEADER)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateDvdReadStructure - output buffer " "cannot hold header information\n")); status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(READ_DVD_STRUCTURES_HEADER); } else if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > MAXUSHORT) { // key length must fit in two bytes TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateDvdReadStructure - output buffer " "too large\n")); status = STATUS_INVALID_PARAMETER; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength & DeviceExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; } else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD) { // reject the request if it's not a DVD device. status = STATUS_INVALID_DEVICE_REQUEST; } } return status; } NTSTATUS RequestValidateDvdStartSession( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_DVD_START_SESSION Arguments: DeviceExtension - device context RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; *DataLength = 0; if (NT_SUCCESS(status)) { if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(DVD_SESSION_ID)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateDvdStartSession: DVD_START_SESSION - output " "buffer too small\n")); status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(DVD_SESSION_ID); } else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD) { // reject the request if it's not a DVD device. status = STATUS_INVALID_DEVICE_REQUEST; } } return status; } NTSTATUS RequestValidateDvdSendKey( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_DVD_SEND_KEY, IOCTL_DVD_SEND_KEY2 Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDVD_COPY_PROTECT_KEY key = NULL; *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &key, NULL); if (NT_SUCCESS(status)) { if((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY)) || (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != key->KeyLength)) { // // Key is too small to have a header or the key length doesn't // match the input buffer length. Key must be invalid // TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateDvdSendKey: [%p] IOCTL_DVD_SEND_KEY - " "key is too small or does not match KeyLength\n", Request)); status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { // allow only certain key type (non-destructive) to go through // IOCTL_DVD_SEND_KEY (which only requires READ access to the device) if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_SEND_KEY) { if ((key->KeyType != DvdChallengeKey) && (key->KeyType != DvdBusKey2) && (key->KeyType != DvdInvalidateAGID)) { status = STATUS_INVALID_PARAMETER; } } else if ((key->KeyType != DvdChallengeKey) && (key->KeyType != DvdBusKey1) && (key->KeyType != DvdBusKey2) && (key->KeyType != DvdTitleKey) && (key->KeyType != DvdAsf) && (key->KeyType != DvdSetRpcKey) && (key->KeyType != DvdGetRpcKey) && (key->KeyType != DvdDiskKey) && (key->KeyType != DvdInvalidateAGID)) { status = STATUS_INVALID_PARAMETER; } else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD) { // reject the request if it's not a DVD device. status = STATUS_INVALID_DEVICE_REQUEST; } } return status; } NTSTATUS RequestValidateGetConfiguration( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_CDROM_GET_CONFIGURATION Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_CONFIGURATION_HEADER)) { status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(GET_CONFIGURATION_HEADER); } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > 0xffff) { // output buffer is too large status = STATUS_INVALID_BUFFER_SIZE; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength & DeviceExtension->AdapterDescriptor->AlignmentMask) { // buffer is not proper size multiple status = STATUS_INVALID_PARAMETER; } if (NT_SUCCESS(status)) { #if BUILD_WOW64_ENABLED && defined(_WIN64) if (WdfRequestIsFrom32BitProcess(Request)) { PGET_CONFIGURATION_IOCTL_INPUT32 inputBuffer = NULL; if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(GET_CONFIGURATION_IOCTL_INPUT32)) { status = STATUS_INFO_LENGTH_MISMATCH; } // // also verify the arguments are reasonable. // if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if (inputBuffer->Feature > 0xffff) { status = STATUS_INVALID_PARAMETER; } else if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) && (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) && (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL)) { status = STATUS_INVALID_PARAMETER; } else if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1]) { status = STATUS_INVALID_PARAMETER; } } } else #endif { PGET_CONFIGURATION_IOCTL_INPUT inputBuffer = NULL; if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(GET_CONFIGURATION_IOCTL_INPUT)) { status = STATUS_INFO_LENGTH_MISMATCH; } // also verify the arguments are reasonable. if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if (inputBuffer->Feature > 0xffff) { status = STATUS_INVALID_PARAMETER; } else if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) && (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) && (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL)) { status = STATUS_INVALID_PARAMETER; } else if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1]) { status = STATUS_INVALID_PARAMETER; } } } } return status; } NTSTATUS RequestValidateSetSpeed( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_CDROM_SET_SPEED Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PCDROM_SET_SPEED inputBuffer = NULL; ULONG requiredLength = 0; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SET_SPEED)) { status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { // Get the request type using CDROM_SET_SPEED structure status = WdfRequestRetrieveInputBuffer(Request, sizeof(CDROM_SET_SPEED), &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if (inputBuffer->RequestType > CdromSetStreaming) { // Unknown request type. status = STATUS_INVALID_PARAMETER; } else if (inputBuffer->RequestType == CdromSetSpeed) { requiredLength = sizeof(CDROM_SET_SPEED); } else { // Don't send SET STREAMING command if this is not a MMC compliant device if (cdData->Mmc.IsMmc == FALSE) { status = STATUS_INVALID_DEVICE_REQUEST; } requiredLength = sizeof(CDROM_SET_STREAMING); } } if (NT_SUCCESS(status)) { if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < requiredLength) { // Input buffer too small status = STATUS_INFO_LENGTH_MISMATCH; } } return status; } NTSTATUS RequestValidateAacsReadMediaKeyBlock( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_READ_MEDIA_KEY_BLOCK Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PAACS_LAYER_NUMBER layerNumber = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_LAYER_NUMBER)) { status = STATUS_INFO_LENGTH_MISMATCH; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < 8) { // This is a variable-length structure, but we're pretty sure // it can never be less than eight bytes... *DataLength = 8; status = STATUS_BUFFER_TOO_SMALL; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &layerNumber, NULL); } if (NT_SUCCESS(status)) { if (*layerNumber > 255) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsStartSession( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_START_SESSION Arguments: DeviceExtension - device context RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(DVD_SESSION_ID)) { *DataLength = sizeof(DVD_SESSION_ID); status = STATUS_BUFFER_TOO_SMALL; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(DVD_SESSION_ID)) { status = STATUS_INVALID_BUFFER_SIZE; } return status; } NTSTATUS RequestValidateAacsSendCertificate( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_SEND_CERTIFICATE Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PAACS_SEND_CERTIFICATE inputBuffer = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_SEND_CERTIFICATE)) { status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsGetCertificate( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_GET_CERTIFICATE Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PDVD_SESSION_ID sessionId = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID)) { status = STATUS_INFO_LENGTH_MISMATCH; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_CERTIFICATE)) { *DataLength = sizeof(AACS_CERTIFICATE); status = STATUS_BUFFER_TOO_SMALL; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_CERTIFICATE)) { status = STATUS_INVALID_BUFFER_SIZE; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &sessionId, NULL); } if (NT_SUCCESS(status)) { if (*sessionId > MAX_COPY_PROTECT_AGID) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsGetChallengeKey( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_GET_CHALLENGE_KEY Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PDVD_SESSION_ID sessionId = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID)) { status = STATUS_INFO_LENGTH_MISMATCH; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_CHALLENGE_KEY)) { *DataLength = sizeof(AACS_CHALLENGE_KEY); status = STATUS_BUFFER_TOO_SMALL; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_CHALLENGE_KEY)) { status = STATUS_INVALID_BUFFER_SIZE; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &sessionId, NULL); } if (NT_SUCCESS(status)) { if (*sessionId > MAX_COPY_PROTECT_AGID) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsSendChallengeKey( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_SEND_CHALLENGE_KEY Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PAACS_SEND_CHALLENGE_KEY inputBuffer = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_SEND_CHALLENGE_KEY)) { status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsReadVolumeId( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_READ_VOLUME_ID Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PDVD_SESSION_ID sessionId = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID)) { status = STATUS_INFO_LENGTH_MISMATCH; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_VOLUME_ID)) { *DataLength = sizeof(AACS_VOLUME_ID); status = STATUS_BUFFER_TOO_SMALL; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_VOLUME_ID)) { status = STATUS_INVALID_BUFFER_SIZE; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &sessionId, NULL); } if (NT_SUCCESS(status)) { if (*sessionId > MAX_COPY_PROTECT_AGID) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsReadSerialNumber( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_READ_SERIAL_NUMBER Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PDVD_SESSION_ID sessionId = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID)) { status = STATUS_INFO_LENGTH_MISMATCH; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_SERIAL_NUMBER)) { *DataLength = sizeof(AACS_SERIAL_NUMBER); status = STATUS_BUFFER_TOO_SMALL; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_SERIAL_NUMBER)) { status = STATUS_INVALID_BUFFER_SIZE; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &sessionId, NULL); } if (NT_SUCCESS(status)) { if (*sessionId > MAX_COPY_PROTECT_AGID) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsReadMediaId( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_READ_MEDIA_ID Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PDVD_SESSION_ID sessionId = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID)) { status = STATUS_INFO_LENGTH_MISMATCH; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_MEDIA_ID)) { *DataLength = sizeof(AACS_MEDIA_ID); status = STATUS_BUFFER_TOO_SMALL; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_MEDIA_ID)) { status = STATUS_INVALID_BUFFER_SIZE; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &sessionId, NULL); } if (NT_SUCCESS(status)) { if (*sessionId > MAX_COPY_PROTECT_AGID) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsBindingNonce( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_READ_BINDING_NONCE IOCTL_AACS_GENERATE_BINDING_NONCE Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PAACS_READ_BINDING_NONCE inputBuffer = NULL; *DataLength = 0; if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(AACS_READ_BINDING_NONCE)) { status = STATUS_INFO_LENGTH_MISMATCH; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(AACS_BINDING_NONCE)) { *DataLength = sizeof(AACS_BINDING_NONCE); status = STATUS_BUFFER_TOO_SMALL; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > sizeof(AACS_BINDING_NONCE)) { status = STATUS_INVALID_BUFFER_SIZE; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if (inputBuffer->SessionId > MAX_COPY_PROTECT_AGID) { status = STATUS_INVALID_PARAMETER; } else if (inputBuffer->NumberOfSectors > 255) { status = STATUS_INVALID_PARAMETER; } else if (inputBuffer->StartLba > MAXULONG) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateExclusiveAccess( _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_CDROM_EXCLUSIVE_ACCESS Arguments: Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL; *DataLength = 0; if (KeGetCurrentIrql() != PASSIVE_LEVEL) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: IOCTL must be called at passive level.\n")); status = STATUS_INVALID_DEVICE_REQUEST; } else if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_EXCLUSIVE_ACCESS)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Input buffer too small\n")); status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &exclusiveAccess, NULL); } if (NT_SUCCESS(status)) { switch (exclusiveAccess->RequestType) { case ExclusiveAccessQueryState: { if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_EXCLUSIVE_LOCK_STATE)) { // // Output buffer too small. // TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Output buffer too small\n")); *DataLength = sizeof(CDROM_EXCLUSIVE_LOCK_STATE); status = STATUS_BUFFER_TOO_SMALL; } break; } case ExclusiveAccessLockDevice: { if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_EXCLUSIVE_LOCK)) { // // Input buffer too small // TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Input buffer too small\n")); status = STATUS_INFO_LENGTH_MISMATCH; } break; } case ExclusiveAccessUnlockDevice: { // // Nothing to check // break; } default: { // // Unknown request type. // TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestValidateExclusiveAccess: Invalid request type\n")); status = STATUS_INVALID_PARAMETER; } } } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleExclusiveAccessQueryLockState( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request ) /*++ Routine Description: Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessQueryState Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); PCDROM_DATA cdData = &deviceExtension->DeviceAdditionalData; PCDROM_EXCLUSIVE_LOCK_STATE exclusiveLockState = NULL; PAGED_CODE(); status = WdfRequestRetrieveOutputBuffer(Request, sizeof(CDROM_EXCLUSIVE_LOCK_STATE), &exclusiveLockState, NULL); NT_ASSERT(NT_SUCCESS(status)); RtlZeroMemory(exclusiveLockState, sizeof(CDROM_EXCLUSIVE_LOCK_STATE)); if (EXCLUSIVE_MODE(cdData)) { // Device is locked for exclusive use exclusiveLockState->LockState = TRUE; RtlCopyMemory(&exclusiveLockState->CallerName, &cdData->CallerName, CDROM_EXCLUSIVE_CALLER_LENGTH); } else { // Device is not locked exclusiveLockState->LockState = FALSE; } RequestCompletion(deviceExtension, Request, status, sizeof(CDROM_EXCLUSIVE_LOCK_STATE)); return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleExclusiveAccessLockDevice( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request ) /*++ Routine Description: Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessLockDevice Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); PCDROM_DATA cdData = &deviceExtension->DeviceAdditionalData; PCDROM_EXCLUSIVE_LOCK exclusiveLock = NULL; PIO_ERROR_LOG_PACKET logEntry; WDFFILEOBJECT fileObject = NULL; ULONG idx = 0; ULONG nameLength = 0; PAGED_CODE(); fileObject = WdfRequestGetFileObject(Request); if (fileObject == NULL) { status = STATUS_INVALID_HANDLE; TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessLockDevice: FileObject is NULL, cannot grant exclusive access\n")); } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, sizeof(CDROM_EXCLUSIVE_LOCK), &exclusiveLock, NULL); } if (NT_SUCCESS(status)) { // Validate the caller name string for (idx = 0; (idx < CDROM_EXCLUSIVE_CALLER_LENGTH) && (exclusiveLock->CallerName[idx] != '\0'); idx++) { if (!ValidChar(exclusiveLock->CallerName[idx])) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessLockDevice: Invalid characters in caller name\n")); // error out status = STATUS_INVALID_PARAMETER; break; } } } if (NT_SUCCESS(status)) { if ((idx == 0) || (idx >= CDROM_EXCLUSIVE_CALLER_LENGTH)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessLockDevice: Not a valid null terminated string.\n")); //error out status = STATUS_INVALID_PARAMETER; } else { nameLength = idx+1; // Add 1 for the NULL character NT_ASSERT(nameLength <= CDROM_EXCLUSIVE_CALLER_LENGTH); } } // If the file system is still mounted on this device fail the request, // unless the force lock flag is set. if (NT_SUCCESS(status)) { if ((TEST_FLAG(exclusiveLock->Access.Flags, CDROM_LOCK_IGNORE_VOLUME) == FALSE) && IsVolumeMounted(deviceExtension->DeviceObject)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessLockDevice: Unable to lock device, file system mounted\n")); status = STATUS_INVALID_DEVICE_STATE; } } // Lock the device for exclusive access if the device is not already locked if (NT_SUCCESS(status)) { if (InterlockedCompareExchangePointer((PVOID)&cdData->ExclusiveOwner, (PVOID)fileObject, NULL) == NULL) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessLockDevice: Entering exclusive mode! Device locked by file object %p\n", fileObject)); // Zero out the CallerName before storing it in the extension RtlZeroMemory(&cdData->CallerName, CDROM_EXCLUSIVE_CALLER_LENGTH); RtlCopyMemory(&cdData->CallerName, &exclusiveLock->CallerName, nameLength); // Send Exclusive Lock notification DeviceSendNotification(deviceExtension, &GUID_IO_CDROM_EXCLUSIVE_LOCK, 0, NULL); // Log an informational event with the caller name logEntry = IoAllocateErrorLogEntry( deviceExtension->DeviceObject, sizeof(IO_ERROR_LOG_PACKET) + CDROM_EXCLUSIVE_CALLER_LENGTH); if (logEntry != NULL) { PUCHAR dumpDataPtr = (PUCHAR) logEntry->DumpData; logEntry->FinalStatus = STATUS_SUCCESS; logEntry->ErrorCode = IO_CDROM_EXCLUSIVE_LOCK; logEntry->SequenceNumber = 0; logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL; logEntry->IoControlCode = IOCTL_CDROM_EXCLUSIVE_ACCESS; logEntry->RetryCount = 0; logEntry->UniqueErrorValue = 0x1; logEntry->DumpDataSize = CDROM_EXCLUSIVE_CALLER_LENGTH; RtlCopyMemory(dumpDataPtr, (PUCHAR)&cdData->CallerName, CDROM_EXCLUSIVE_CALLER_LENGTH); // Write the error log packet. IoWriteErrorLogEntry(logEntry); } } else { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessLockDevice: Unable to lock device, device already locked.\n")); status = STATUS_ACCESS_DENIED; } } RequestCompletion(deviceExtension, Request, status, 0); return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleExclusiveAccessUnlockDevice( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request ) /*++ Routine Description: Handle request of IOCTL_CDROM_EXCLUSIVE_ACCESS with ExclusiveAccessUnlockDevice Arguments: Device - device handle Request - request to be handled Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); PCDROM_EXCLUSIVE_ACCESS exclusiveAccess = NULL; WDFFILEOBJECT fileObject = NULL; PAGED_CODE(); fileObject = WdfRequestGetFileObject(Request); if (fileObject == NULL) { // The device can be unlocked from exclusive mode only via the file object which locked it. status = STATUS_INVALID_HANDLE; TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessUnlockDevice: FileObject is NULL, cannot release exclusive access\n")); } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, sizeof(PCDROM_EXCLUSIVE_ACCESS), &exclusiveAccess, NULL); } if (NT_SUCCESS(status)) { status = DeviceUnlockExclusive(deviceExtension, fileObject, TEST_FLAG(exclusiveAccess->Flags, CDROM_NO_MEDIA_NOTIFICATIONS)); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleExclusiveAccessUnlockDevice: Device unlocked\n")); } RequestCompletion(deviceExtension, Request, status, 0); return status; } NTSTATUS RequestHandleQueryPropertyRetrieveCachedData( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_QUERY_PROPERTY when the required data is cached. Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PSTORAGE_PROPERTY_QUERY inputBuffer = NULL; *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); if (NT_SUCCESS(status)) { if (inputBuffer->PropertyId == StorageDeviceProperty) { // check output buffer length if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0) { // According to MSDN, an output buffer of size 0 can be used to determine if a property exists // so this must be a success case with no data transferred *DataLength = 0; status = STATUS_SUCCESS; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER)) { // Buffer too small *DataLength = DeviceExtension->DeviceDescriptor->Size; status = STATUS_BUFFER_TOO_SMALL; } else { PSTORAGE_DEVICE_DESCRIPTOR outputDescriptor = NULL; CHAR* localDescriptorBuffer = (CHAR*)DeviceExtension->DeviceDescriptor; status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputDescriptor, NULL); if (NT_SUCCESS(status)) { // transfer as much data out as the buffer will allow *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, DeviceExtension->DeviceDescriptor->Size); RtlCopyMemory(outputDescriptor, DeviceExtension->DeviceDescriptor, *DataLength); // walk through and update offset variables to reflect data that didn't make it into the output buffer if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, VendorIdOffset)) && (DeviceExtension->DeviceDescriptor->VendorIdOffset != 0) && (DeviceExtension->DeviceDescriptor->VendorIdOffset != 0xFFFFFFFF)) { // set VendorIdOffset appropriately if (*DataLength < (DeviceExtension->DeviceDescriptor->VendorIdOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->VendorIdOffset))) { outputDescriptor->VendorIdOffset = 0; } } if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, ProductIdOffset)) && (DeviceExtension->DeviceDescriptor->ProductIdOffset != 0) && (DeviceExtension->DeviceDescriptor->ProductIdOffset != 0xFFFFFFFF)) { // set ProductIdOffset appropriately if (*DataLength < (DeviceExtension->DeviceDescriptor->ProductIdOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->ProductIdOffset))) { outputDescriptor->ProductIdOffset = 0; } } if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, ProductRevisionOffset)) && (DeviceExtension->DeviceDescriptor->ProductRevisionOffset != 0) && (DeviceExtension->DeviceDescriptor->ProductRevisionOffset != 0xFFFFFFFF)) { // set ProductRevisionOffset appropriately if (*DataLength < (DeviceExtension->DeviceDescriptor->ProductRevisionOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->ProductRevisionOffset))) { outputDescriptor->ProductRevisionOffset = 0; } } if ((*DataLength >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_DEVICE_DESCRIPTOR, SerialNumberOffset)) && (DeviceExtension->DeviceDescriptor->SerialNumberOffset != 0) && (DeviceExtension->DeviceDescriptor->SerialNumberOffset != 0xFFFFFFFF)) { // set SerialNumberOffset appropriately if (*DataLength < (DeviceExtension->DeviceDescriptor->SerialNumberOffset + strlen(localDescriptorBuffer + DeviceExtension->DeviceDescriptor->SerialNumberOffset))) { // NOTE: setting this to 0 since that is what most port drivers do // [this could cause issues with SCSI port devices whose clients expect -1 in this field] outputDescriptor->SerialNumberOffset = 0; } } status = STATUS_SUCCESS; } } } //end of StorageDeviceProperty else if (inputBuffer->PropertyId == StorageAdapterProperty) { if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength == 0) { // According to MSDN, an output buffer of size 0 can be used to determine if a property exists // so this must be a success case with no data transferred *DataLength = 0; status = STATUS_SUCCESS; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER)) { // Buffer too small *DataLength = DeviceExtension->AdapterDescriptor->Size; status = STATUS_BUFFER_TOO_SMALL; } else { PSTORAGE_ADAPTER_DESCRIPTOR outputDescriptor = NULL; status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputDescriptor, NULL); if (NT_SUCCESS(status)) { // copy as much data out as the buffer will allow *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, DeviceExtension->AdapterDescriptor->Size); RtlCopyMemory(outputDescriptor, DeviceExtension->AdapterDescriptor, *DataLength); // set status status = STATUS_SUCCESS; } } } } return status; } NTSTATUS RequestHandleQueryPropertyDeviceUniqueId( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request ) /*++ Routine Description: Handle request of IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceUniqueIdProperty. Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); PSTORAGE_PROPERTY_QUERY inputBuffer = NULL; PSTORAGE_DESCRIPTOR_HEADER descHeader = NULL; size_t outLength = 0; WDF_REQUEST_PARAMETERS requestParameters; // Get the Request parameters WDF_REQUEST_PARAMETERS_INIT(&requestParameters); WdfRequestGetParameters(Request, &requestParameters); status = WdfRequestRetrieveInputBuffer(Request, requestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); if (NT_SUCCESS(status)) { BOOLEAN overflow = FALSE; BOOLEAN infoFound = FALSE; // Must run at less then dispatch. if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { NT_ASSERT(FALSE); outLength = 0; status = STATUS_INVALID_LEVEL; } else if (inputBuffer->QueryType == PropertyExistsQuery) { outLength = 0; status = STATUS_SUCCESS; } else if (inputBuffer->QueryType != PropertyStandardQuery) { outLength = 0; status = STATUS_NOT_SUPPORTED; } else { // Check AdditionalParameters validity. if (inputBuffer->AdditionalParameters[0] == DUID_INCLUDE_SOFTWARE_IDS) { // Do nothing } else if (inputBuffer->AdditionalParameters[0] == DUID_HARDWARE_IDS_ONLY) { // Do nothing } else { outLength = 0; status = STATUS_INVALID_PARAMETER; } if (NT_SUCCESS(status) && (outLength < sizeof(STORAGE_DESCRIPTOR_HEADER))) { outLength = 0; status = STATUS_INFO_LENGTH_MISMATCH; } } // From this point forward the status depends on the overflow // and infoFound flags. if (NT_SUCCESS(status)) { outLength = requestParameters.Parameters.DeviceIoControl.OutputBufferLength; status = WdfRequestRetrieveOutputBuffer(Request, requestParameters.Parameters.DeviceIoControl.OutputBufferLength, &descHeader, NULL); } if (NT_SUCCESS(status)) { RtlZeroMemory(descHeader, outLength); descHeader->Version = DUID_VERSION_1; descHeader->Size = sizeof(STORAGE_DEVICE_UNIQUE_IDENTIFIER); // Try to build device unique id from StorageDeviceIdProperty. status = RequestDuidGetDeviceIdProperty(deviceExtension, Request, requestParameters, &outLength); if (status == STATUS_BUFFER_OVERFLOW) { overflow = TRUE; } if (NT_SUCCESS(status)) { infoFound = TRUE; } // Try to build device unique id from StorageDeviceProperty. status = RequestDuidGetDeviceProperty(deviceExtension, Request, requestParameters, &outLength); if (status == STATUS_BUFFER_OVERFLOW) { overflow = TRUE; } if (NT_SUCCESS(status)) { infoFound = TRUE; } // Return overflow, success, or a generic error. if (overflow) { // If output buffer is STORAGE_DESCRIPTOR_HEADER, then return // success to the user. Otherwise, send an error so the user // knows a larger buffer is required. if (outLength == sizeof(STORAGE_DESCRIPTOR_HEADER)) { status = STATUS_SUCCESS; } else { outLength = (ULONG)WdfRequestGetInformation(Request); status = STATUS_BUFFER_OVERFLOW; } } else if (infoFound) { status = STATUS_SUCCESS; // Exercise the compare routine. This should always succeed. NT_ASSERT(DuidExactMatch == CompareStorageDuids((PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader, (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader)); } else { status = STATUS_NOT_FOUND; } } } RequestCompletion(deviceExtension, Request, status, outLength); return status; } NTSTATUS RequestHandleQueryPropertyWriteCache( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request ) /*++ Routine Description: Handle request of IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceWriteCacheProperty. Arguments: DeviceExtension - device context Request - request to be handled Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); PSTORAGE_PROPERTY_QUERY query = NULL; PSTORAGE_WRITE_CACHE_PROPERTY writeCache = NULL; PMODE_PARAMETER_HEADER modeData = NULL; PMODE_CACHING_PAGE pageData = NULL; size_t length = 0; ULONG information = 0; PSCSI_REQUEST_BLOCK srb = NULL; WDF_REQUEST_PARAMETERS requestParameters; // Get the Request parameters WDF_REQUEST_PARAMETERS_INIT(&requestParameters); WdfRequestGetParameters(Request, &requestParameters); status = WdfRequestRetrieveInputBuffer(Request, requestParameters.Parameters.DeviceIoControl.InputBufferLength, &query, NULL); if (NT_SUCCESS(status)) { // Must run at less then dispatch. if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { NT_ASSERT(FALSE); status = STATUS_INVALID_LEVEL; } else if (query->QueryType == PropertyExistsQuery) { information = 0; status = STATUS_SUCCESS; } else if (query->QueryType != PropertyStandardQuery) { status = STATUS_NOT_SUPPORTED; } } if (NT_SUCCESS(status)) { length = requestParameters.Parameters.DeviceIoControl.OutputBufferLength; if (length < sizeof(STORAGE_DESCRIPTOR_HEADER)) { status = STATUS_INFO_LENGTH_MISMATCH; } } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, requestParameters.Parameters.DeviceIoControl.OutputBufferLength, &writeCache, NULL); } if (NT_SUCCESS(status)) { RtlZeroMemory(writeCache, length); // Set version and required size. writeCache->Version = sizeof(STORAGE_WRITE_CACHE_PROPERTY); writeCache->Size = sizeof(STORAGE_WRITE_CACHE_PROPERTY); if (length < sizeof(STORAGE_WRITE_CACHE_PROPERTY)) { // caller only wants header information, bail out. information = sizeof(STORAGE_DESCRIPTOR_HEADER); status = STATUS_SUCCESS; RequestCompletion(deviceExtension, Request, status, information); return status; } } if (NT_SUCCESS(status)) { srb = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK) + (sizeof(ULONG_PTR) * 2), CDROM_TAG_SRB); if (srb == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } } if (NT_SUCCESS(status)) { RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); // Set known values writeCache->NVCacheEnabled = FALSE; writeCache->UserDefinedPowerProtection = TEST_FLAG(deviceExtension->DeviceFlags, DEV_POWER_PROTECTED); // Check for flush cache support by sending a sync cache command // to the device. // Set timeout value and mark the request as not being a tagged request. srb->Length = SCSI_REQUEST_BLOCK_SIZE; srb->TimeOutValue = TimeOutValueGetCapValue(deviceExtension->TimeOutValue, 4); srb->QueueTag = SP_UNTAGGED; srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; srb->SrbFlags = deviceExtension->SrbFlags; srb->Function = SRB_FUNCTION_EXECUTE_SCSI; srb->CdbLength = 10; srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE; status = DeviceSendSrbSynchronously(Device, srb, NULL, 0, TRUE, //flush drive cache Request); if (NT_SUCCESS(status)) { writeCache->FlushCacheSupported = TRUE; } else { // Device does not support sync cache TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Synchronize cache failed with status 0x%X\n", status)); writeCache->FlushCacheSupported = FALSE; // Reset the status if there was any failure status = STATUS_SUCCESS; } modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, MODE_PAGE_DATA_SIZE, CDROM_TAG_MODE_DATA); if (modeData == NULL) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to allocate mode data buffer\n")); status = STATUS_INSUFFICIENT_RESOURCES; } } if (NT_SUCCESS(status)) { RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE); length = DeviceRetrieveModeSenseUsingScratch(deviceExtension, (PCHAR)modeData, MODE_PAGE_DATA_SIZE, MODE_PAGE_CACHING, MODE_SENSE_CURRENT_VALUES); if (length < sizeof(MODE_PARAMETER_HEADER)) { // Retry the request in case of a check condition. length = DeviceRetrieveModeSenseUsingScratch(deviceExtension, (PCHAR)modeData, MODE_PAGE_DATA_SIZE, MODE_PAGE_CACHING, MODE_SENSE_CURRENT_VALUES); if (length < sizeof(MODE_PARAMETER_HEADER)) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Mode Sense failed\n")); status = STATUS_IO_DEVICE_ERROR; } } } if (NT_SUCCESS(status)) { // If the length is greater than length indicated by the mode data reset // the data to the mode data. if (length > (ULONG) (modeData->ModeDataLength + 1)) { length = modeData->ModeDataLength + 1; } // Look for caching page in the returned mode page data. pageData = ModeSenseFindSpecificPage((PCHAR)modeData, length, MODE_PAGE_CACHING, TRUE); // Check if valid caching page exists. if (pageData == NULL) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to find caching mode page.\n")); // Set write cache value as unknown. writeCache->WriteCacheEnabled = WriteCacheEnableUnknown; writeCache->WriteCacheType = WriteCacheTypeUnknown; } else { writeCache->WriteCacheEnabled = pageData->WriteCacheEnable ? WriteCacheEnabled : WriteCacheDisabled; writeCache->WriteCacheType = pageData->WriteCacheEnable ? WriteCacheTypeWriteBack : WriteCacheTypeUnknown; } // Check write through support. if (modeData->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED) { writeCache->WriteThroughSupported = WriteThroughSupported; } else { writeCache->WriteThroughSupported = WriteThroughNotSupported; } // Get the changeable caching mode page and check write cache is changeable. RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE); length = DeviceRetrieveModeSenseUsingScratch(deviceExtension, (PCHAR) modeData, MODE_PAGE_DATA_SIZE, MODE_PAGE_CACHING, MODE_SENSE_CHANGEABLE_VALUES); if (length < sizeof(MODE_PARAMETER_HEADER)) { // Retry the request in case of a check condition. length = DeviceRetrieveModeSenseUsingScratch(deviceExtension, (PCHAR) modeData, MODE_PAGE_DATA_SIZE, MODE_PAGE_CACHING, MODE_SENSE_CHANGEABLE_VALUES); if (length < sizeof(MODE_PARAMETER_HEADER)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Mode Sense failed\n")); // If the device fails to return changeable pages, then // set the write cache changeable value to unknown. writeCache->WriteCacheChangeable = WriteCacheChangeUnknown; information = sizeof(STORAGE_WRITE_CACHE_PROPERTY); } } } if (NT_SUCCESS(status)) { // If the length is greater than length indicated by the mode data reset // the data to the mode data. if (length > (ULONG) (modeData->ModeDataLength + 1)) { length = modeData->ModeDataLength + 1; } // Look for caching page in the returned mode page data. pageData = ModeSenseFindSpecificPage((PCHAR)modeData, length, MODE_PAGE_CACHING, TRUE); // Check if valid caching page exists. if (pageData == NULL) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "RequestHandleQueryPropertyWriteCache: Unable to find caching mode page.\n")); // Set write cache changeable value to unknown. writeCache->WriteCacheChangeable = WriteCacheChangeUnknown; } else { writeCache->WriteCacheChangeable = pageData->WriteCacheEnable ? WriteCacheChangeable : WriteCacheNotChangeable; } information = sizeof(STORAGE_WRITE_CACHE_PROPERTY); } FREE_POOL(srb); FREE_POOL(modeData); RequestCompletion(deviceExtension, Request, status, information); return status; } NTSTATUS RequestValidateDvdReadKey( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_DVD_READ_KEY Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDVD_COPY_PROTECT_KEY keyParameters = NULL; ULONG keyLength = 0; *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &keyParameters, NULL); if (NT_SUCCESS(status)) { if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DvdDeviceControl: EstablishDriveKey - challenge " "key buffer too small\n")); status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { switch(keyParameters->KeyType) { case DvdChallengeKey: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_CHALLENGE_KEY_LENGTH); keyLength = DVD_CHALLENGE_KEY_LENGTH; break; } case DvdBusKey1: case DvdBusKey2: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_BUS_KEY_LENGTH); keyLength = DVD_BUS_KEY_LENGTH; break; } case DvdTitleKey: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_TITLE_KEY_LENGTH); keyLength = DVD_TITLE_KEY_LENGTH; break; } case DvdAsf: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_ASF_LENGTH); keyLength = DVD_ASF_LENGTH; break; } case DvdDiskKey: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_DISK_KEY_LENGTH); keyLength = DVD_DISK_KEY_LENGTH; break; } case DvdGetRpcKey: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_RPC_KEY_LENGTH); keyLength = DVD_RPC_KEY_LENGTH; break; } default: { keyLength = sizeof(DVD_COPY_PROTECT_KEY); break; } } if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < keyLength) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DvdDeviceControl: EstablishDriveKey - output " "buffer too small\n")); status = STATUS_BUFFER_TOO_SMALL; *DataLength = keyLength; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength & DeviceExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; } else if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD) { // reject the request if it's not a DVD device. status = STATUS_INVALID_DEVICE_REQUEST; } } return status; } NTSTATUS RequestValidateDvdEndSession( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DVD_END_SESSION Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDVD_SESSION_ID sessionId = NULL; UNREFERENCED_PARAMETER(DeviceExtension); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &sessionId, NULL); if (NT_SUCCESS(status)) { if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_SESSION_ID)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DvdDeviceControl: EndSession - input buffer too " "small\n")); status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateAacsEndSession( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_AACS_END_SESSION Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDVD_SESSION_ID sessionId = NULL; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &sessionId, NULL); if (NT_SUCCESS(status)) { if (!cdData->Mmc.IsAACS) { status = STATUS_INVALID_DEVICE_REQUEST; } else if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength != sizeof(DVD_SESSION_ID)) { status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateEnableStreaming( _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validates an IOCTL_CDROM_ENABLE_STREAMING request Arguments: Request - request to be handled RequestParameters - request parameters DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_STREAMING_CONTROL inputBuffer = NULL; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_STREAMING_CONTROL)) { status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { // Get the request type using CDROM_STREAMING_CONTROL structure status = WdfRequestRetrieveInputBuffer(Request, sizeof(CDROM_STREAMING_CONTROL), &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if (inputBuffer->RequestType != CdromStreamingDisable && inputBuffer->RequestType != CdromStreamingEnableForReadOnly && inputBuffer->RequestType != CdromStreamingEnableForWriteOnly && inputBuffer->RequestType != CdromStreamingEnableForReadWrite) { // Unknown request type status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateSendOpcInformation( _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validates an IOCTL_CDROM_SEND_OPC_INFORMATION request Arguments: Request - request to be handled RequestParameters - request parameters DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_SIMPLE_OPC_INFO inputBuffer = NULL; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SIMPLE_OPC_INFO)) { status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { // Get the request type using CDROM_SIMPLE_OPC_INFO structure status = WdfRequestRetrieveInputBuffer(Request, sizeof(CDROM_SIMPLE_OPC_INFO), &inputBuffer, NULL); } if (NT_SUCCESS(status)) { if (inputBuffer->RequestType != SimpleOpcInfo) { // Unknown request type status = STATUS_INVALID_PARAMETER; } } return status; } NTSTATUS RequestValidateGetPerformance( _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validates an IOCTL_CDROM_GET_PERFORMANCE request Arguments: Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_WRITE_SPEED_REQUEST writeSpeedRequest = NULL; PCDROM_PERFORMANCE_REQUEST performanceRequest = NULL; *DataLength = 0; // CDROM_WRITE_SPEED_REQUEST is the smallest performance request that we support. // We use it to retrieve request type and then check input length more carefully // on a per request type basis. if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_WRITE_SPEED_REQUEST)) { status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, sizeof(CDROM_WRITE_SPEED_REQUEST), (PVOID*)&writeSpeedRequest, NULL); } if (NT_SUCCESS(status)) { if (writeSpeedRequest->RequestType == CdromPerformanceRequest) { // CDROM_PERFORMANCE_REQUEST is bigger than CDROM_WRITE_SPEED_REQUEST, // so we perform more checks and retrieve more bytes through WDF. if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_PERFORMANCE_REQUEST)) { status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, sizeof(CDROM_PERFORMANCE_REQUEST), &performanceRequest, NULL); } if (!NT_SUCCESS(status)) { // just pass the status code from above } // validate all enum-type fields of CDROM_PERFORMANCE_REQUEST else if (performanceRequest->PerformanceType != CdromReadPerformance && performanceRequest->PerformanceType != CdromWritePerformance) { status = STATUS_INVALID_PARAMETER; } else if (performanceRequest->Exceptions != CdromNominalPerformance && performanceRequest->Exceptions != CdromEntirePerformanceList && performanceRequest->Exceptions != CdromPerformanceExceptionsOnly) { status = STATUS_INVALID_PARAMETER; } else if (performanceRequest->Tolerance != Cdrom10Nominal20Exceptions) { status = STATUS_INVALID_PARAMETER; } } else if (writeSpeedRequest->RequestType == CdromWriteSpeedRequest) { // No additional checks here: all remaining fields are ignored // if RequestType == CdromWriteSpeedRequest. } else { status = STATUS_INVALID_PARAMETER; } } // finally, check output buffer length if (NT_SUCCESS(status)) { if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_PERFORMANCE_HEADER)) { status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(CDROM_PERFORMANCE_HEADER); } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength > ((USHORT)-1)) { status = STATUS_INVALID_PARAMETER; } } return status; } _IRQL_requires_max_(APC_LEVEL) PCDB RequestGetScsiPassThroughCdb( _In_ PIRP Irp ) /*++ Routine Description: Get the CDB structure from the SCSI pass through Arguments: Irp - request to be handled Return Value: PCDB --*/ { PCDB cdb = NULL; PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); ULONG inputBufferLength = 0; PVOID inputBuffer = NULL; BOOLEAN legacyPassThrough = FALSE; PAGED_CODE(); if (((currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) || (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) || (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_EX) || (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX)) && (Irp->AssociatedIrp.SystemBuffer != NULL)) { inputBufferLength = currentIrpStack->Parameters.DeviceIoControl.InputBufferLength; inputBuffer = Irp->AssociatedIrp.SystemBuffer; legacyPassThrough = TRUE; if ((currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_EX) || (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX)) { legacyPassThrough = FALSE; } // // If this is a 32 bit application running on 64 bit then thunk the // input structures to grab the cdb. // #if BUILD_WOW64_ENABLED && defined(_WIN64) if (IoIs32bitProcess(Irp)) { if (legacyPassThrough) { if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH32)) { cdb = (PCDB)((PSCSI_PASS_THROUGH32)inputBuffer)->Cdb; } } else { if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH32_EX)) { cdb = (PCDB)((PSCSI_PASS_THROUGH32_EX)inputBuffer)->Cdb; } } } else #endif { if (legacyPassThrough) { if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH)) { cdb = (PCDB)((PSCSI_PASS_THROUGH)inputBuffer)->Cdb; } } else { if (inputBufferLength >= sizeof(SCSI_PASS_THROUGH_EX)) { cdb = (PCDB)((PSCSI_PASS_THROUGH_EX)inputBuffer)->Cdb; } } } } return cdb; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleScsiPassThrough( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request ) /*++ Routine Description: Handle request of IOCTL_SCSI_PASS_THROUGH IOCTL_SCSI_PASS_THROUGH_DIRECT The function sets the MinorFunction field of irpStack, and pass the request to lower level driver. Arguments: Device - device object Request - request to be handled Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); PIRP irp = WdfRequestWdmGetIrp(Request); PZERO_POWER_ODD_INFO zpoddInfo = deviceExtension->ZeroPowerODDInfo; PCDB cdb = NULL; BOOLEAN isSoftEject = FALSE; #if DBG PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request); #endif PAGED_CODE(); #if DBG // SPTI is always processed in sync manner. NT_ASSERT(requestContext->SyncRequired); #endif if ((zpoddInfo != NULL) && (zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) && (zpoddInfo->Load == 0)) // Drawer { cdb = RequestGetScsiPassThroughCdb(irp); if ((cdb != NULL) && (cdb->AsByte[0] == SCSIOP_START_STOP_UNIT) && (cdb->START_STOP.LoadEject == 1) && (cdb->START_STOP.Start == 0)) { isSoftEject = TRUE; } } WdfRequestFormatRequestUsingCurrentType(Request); // Special for SPTI, set the MinorFunction. { PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(irp); nextStack->MinorFunction = 1; } status = RequestSend(deviceExtension, Request, deviceExtension->IoTarget, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, NULL); if (!NT_SUCCESS(status) && (isSoftEject != FALSE)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "RequestHandleScsiPassThrough: soft eject detected, device marked as active\n")); DeviceMarkActive(deviceExtension, TRUE, FALSE); } RequestCompletion(deviceExtension, Request, status, WdfRequestGetInformation(Request)); return status; } NTSTATUS RequestHandleMountQueryUniqueId( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_MOUNTDEV_QUERY_UNIQUE_ID Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PMOUNTDEV_UNIQUE_ID uniqueId = NULL; *DataLength = 0; if (!DeviceExtension->MountedDeviceInterfaceName.Buffer) { status = STATUS_INVALID_PARAMETER; } else if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) { *DataLength = sizeof(MOUNTDEV_UNIQUE_ID); status = STATUS_BUFFER_TOO_SMALL; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &uniqueId, NULL); } if (NT_SUCCESS(status)) { RtlZeroMemory(uniqueId, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength); uniqueId->UniqueIdLength = DeviceExtension->MountedDeviceInterfaceName.Length; if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < (sizeof(USHORT) + DeviceExtension->MountedDeviceInterfaceName.Length)) { *DataLength = sizeof(MOUNTDEV_UNIQUE_ID); status = STATUS_BUFFER_OVERFLOW; } } if (NT_SUCCESS(status)) { RtlCopyMemory(uniqueId->UniqueId, DeviceExtension->MountedDeviceInterfaceName.Buffer, uniqueId->UniqueIdLength); *DataLength = sizeof(USHORT) + uniqueId->UniqueIdLength; status = STATUS_SUCCESS; } return status; } NTSTATUS RequestHandleMountQueryDeviceName( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_MOUNTDEV_QUERY_DEVICE_NAME Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PMOUNTDEV_NAME name = NULL; *DataLength = 0; NT_ASSERT(DeviceExtension->DeviceName.Buffer); if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) { status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(MOUNTDEV_NAME); } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &name, NULL); } if (NT_SUCCESS(status)) { RtlZeroMemory(name, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength); name->NameLength = DeviceExtension->DeviceName.Length; if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < (sizeof(USHORT) + DeviceExtension->DeviceName.Length)) { status = STATUS_BUFFER_OVERFLOW; *DataLength = sizeof(MOUNTDEV_NAME); } } if (NT_SUCCESS(status)) { RtlCopyMemory(name->Name, DeviceExtension->DeviceName.Buffer, name->NameLength); status = STATUS_SUCCESS; *DataLength = sizeof(USHORT) + name->NameLength; } return status; } NTSTATUS RequestHandleMountQuerySuggestedLinkName( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName = NULL; WCHAR driveLetterNameBuffer[10] = {0}; RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0}; PWSTR valueName = NULL; UNICODE_STRING driveLetterName = {0}; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) { status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); } if (NT_SUCCESS(status)) { valueName = ExAllocatePoolWithTag(PagedPool, DeviceExtension->DeviceName.Length + sizeof(WCHAR), CDROM_TAG_STRINGS); if (valueName == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } } if (NT_SUCCESS(status)) { RtlCopyMemory(valueName, DeviceExtension->DeviceName.Buffer, DeviceExtension->DeviceName.Length); valueName[DeviceExtension->DeviceName.Length/sizeof(WCHAR)] = 0; driveLetterName.Buffer = driveLetterNameBuffer; driveLetterName.MaximumLength = sizeof(driveLetterNameBuffer); driveLetterName.Length = 0; queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK; queryTable[0].Name = valueName; queryTable[0].EntryContext = &driveLetterName; queryTable[0].DefaultType = (REG_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\Machine\\System\\DISK", // why hard coded? queryTable, NULL, NULL); } if (NT_SUCCESS(status)) { if ((driveLetterName.Length == 4) && (driveLetterName.Buffer[0] == '%') && (driveLetterName.Buffer[1] == ':')) { driveLetterName.Buffer[0] = 0xFF; } else if ((driveLetterName.Length != 4) || (driveLetterName.Buffer[0] < FirstDriveLetter) || (driveLetterName.Buffer[0] > LastDriveLetter) || (driveLetterName.Buffer[1] != ':')) { status = STATUS_NOT_FOUND; } } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &suggestedName, NULL); } if (NT_SUCCESS(status)) { RtlZeroMemory(suggestedName, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength); suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE; suggestedName->NameLength = 28; *DataLength = FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28; if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < *DataLength) { *DataLength = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME); status = STATUS_BUFFER_OVERFLOW; } } if (NT_SUCCESS(status)) { RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\Machine\\System\\DISK", valueName); RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24); suggestedName->Name[12] = driveLetterName.Buffer[0]; suggestedName->Name[13] = ':'; } FREE_POOL(valueName); return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleReadTOC( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_READ_TOC IOCTL_CDROM_GET_LAST_SESSION Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; VOID* outputBuffer = NULL; PAGED_CODE (); *DataLength = 0; if (DeviceIsPlayActive(DeviceExtension->Device)) { status = STATUS_DEVICE_BUSY; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } // handle the request if (NT_SUCCESS(status)) { size_t transferSize; CDB cdb; transferSize = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, sizeof(CDROM_TOC)); RtlZeroMemory(outputBuffer, transferSize); ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) { // Set format to return first and last session numbers. cdb.READ_TOC.Format2 = CDROM_READ_TOC_EX_FORMAT_SESSION; } else { // Use MSF addressing cdb.READ_TOC.Msf = 1; } cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC; cdb.READ_TOC.AllocationLength[0] = (UCHAR)(transferSize >> 8); cdb.READ_TOC.AllocationLength[1] = (UCHAR)(transferSize & 0xFF); status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, 10); if (NT_SUCCESS(status)) { *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength; RtlCopyMemory(outputBuffer, DeviceExtension->ScratchContext.ScratchBuffer, *DataLength); } ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleReadTocEx( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_READ_TOC_EX Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_READ_TOC_EX inputBuffer = NULL; VOID* outputBuffer = NULL; PAGED_CODE (); *DataLength = 0; if (DeviceIsPlayActive(DeviceExtension->Device)) { status = STATUS_DEVICE_BUSY; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } // handle the request if (NT_SUCCESS(status)) { size_t transferSize; CDB cdb; transferSize = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, MAXUSHORT); ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC; cdb.READ_TOC.Msf = inputBuffer->Msf; cdb.READ_TOC.Format2 = inputBuffer->Format; cdb.READ_TOC.StartingTrack = inputBuffer->SessionTrack; cdb.READ_TOC.AllocationLength[0] = (UCHAR)(transferSize >> 8); cdb.READ_TOC.AllocationLength[1] = (UCHAR)(transferSize & 0xFF); status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, 10); if (NT_SUCCESS(status)) { if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE) { *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; } else { *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength; RtlCopyMemory(outputBuffer, DeviceExtension->ScratchContext.ScratchBuffer, *DataLength); } } ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) VOID GetConfigurationDataConversionTypeAllToTypeOne( _In_ FEATURE_NUMBER RequestedFeature, _In_ PSCSI_REQUEST_BLOCK Srb, _Out_ size_t * DataLength ) /*++ Routine Description: Some CDROM devices do not handle the GET CONFIGURATION commands with TYPE ONE request. The command will time out causing a bus reset. To avoid this problem we set a device flag during start device if the device fails a TYPE ONE request. If this flag is set the TYPE ONE requests will be tried as TYPE ALL request and the data will be converted to TYPE ONE format in this routine. Arguments: RequestedFeature - device context Srb - request to be handled DataLength - transfer data length Return Value: NTSTATUS --*/ { PFEATURE_HEADER featureHeader = NULL; FEATURE_NUMBER thisFeature; ULONG totalLength = 0; ULONG featureLength = 0; ULONG headerLength = 0; PGET_CONFIGURATION_HEADER header = NULL; PAGED_CODE (); *DataLength = 0; if (Srb->DataTransferLength < RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength)) { // do not have valid data. return; } // Calculate the length of valid data available in the // capabilities buffer from the DataLength field header = (PGET_CONFIGURATION_HEADER) Srb->DataBuffer; REVERSE_BYTES(&totalLength, header->DataLength); totalLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength); // Make sure the we have enough data in the SRB totalLength = min(totalLength, Srb->DataTransferLength); // If we have received enough data from the device // check for the given feature. if (totalLength >= (sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER))) { // Feature information is present. Verify the feature. featureHeader = (PFEATURE_HEADER)((PUCHAR)Srb->DataBuffer + sizeof(GET_CONFIGURATION_HEADER)); thisFeature = (featureHeader->FeatureCode[0] << 8) | (featureHeader->FeatureCode[1]); if (thisFeature == RequestedFeature) { // Calculate the feature length featureLength = sizeof(FEATURE_HEADER) + featureHeader->AdditionalLength; } } // Calculate the total size totalLength = sizeof(GET_CONFIGURATION_HEADER) + featureLength; headerLength = totalLength - RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength); REVERSE_BYTES(header->DataLength, &headerLength); *DataLength = totalLength; return; } _IRQL_requires_max_(APC_LEVEL) VOID GetConfigurationDataSynthesize( _In_reads_bytes_(InputBufferSize) PVOID InputBuffer, _In_ ULONG InputBufferSize, _Out_writes_bytes_(OutputBufferSize) PVOID OutputBuffer, _In_ size_t OutputBufferSize, _In_ FEATURE_NUMBER StartingFeature, _In_ ULONG RequestType, _Out_ size_t * DataLength ) /*++ Routine Description: Get Configuration is a frequently used command, and we don't want it to wake up the device in case it is in Zero Power state. Before entering Zero Power state, the complete response of the command is saved in cache, and since the response is always the same in case there is no media, we can synthesize the response based on the user request. Arguments: InputBuffer - buffer containing cached command response InputBufferSize - size of above buffer OutputBuffer - buffer to fill in result OutputBufferSize - size of above buffer StartingFeature - requested Starting Feature Number RequestType - requested Request Type DataLength - transfer data length Return Value: --*/ { PFEATURE_HEADER featureHeader = NULL; ULONG validLength = 0; ULONG featureLength = 0; ULONG headerLength = 0; PUCHAR buffer = NULL; ULONG bytesRemaining = 0; FEATURE_NUMBER featureCode = 0; BOOLEAN shouldCopy = FALSE; size_t copyLength = 0; size_t transferedLength = 0; size_t requiredLength = 0; PGET_CONFIGURATION_HEADER header = NULL; PAGED_CODE (); if (InputBufferSize < sizeof (GET_CONFIGURATION_HEADER)) { // do not have valid data. *DataLength = 0; return; } // Calculate the length of valid data available in the // capabilities buffer from the DataLength field header = (PGET_CONFIGURATION_HEADER) InputBuffer; REVERSE_BYTES(&validLength, header->DataLength); validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength); // Make sure we have enough data validLength = min(validLength, InputBufferSize); // Copy the header first copyLength = min(OutputBufferSize, sizeof (GET_CONFIGURATION_HEADER)); RtlMoveMemory(OutputBuffer, InputBuffer, copyLength); transferedLength = copyLength; requiredLength = sizeof (GET_CONFIGURATION_HEADER); if (validLength > sizeof (GET_CONFIGURATION_HEADER)) { buffer = header->Data; bytesRemaining = validLength - sizeof (GET_CONFIGURATION_HEADER); // Ignore incomplete feature descriptor while (bytesRemaining >= sizeof (FEATURE_HEADER)) { featureHeader = (PFEATURE_HEADER) buffer; shouldCopy = FALSE; featureCode = (featureHeader->FeatureCode[0] << 8) | (featureHeader->FeatureCode[1]); featureLength = sizeof (FEATURE_HEADER) + featureHeader->AdditionalLength; if (featureCode >= StartingFeature) { switch (RequestType) { case SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL: shouldCopy = TRUE; break; case SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT: if (featureHeader->Current) { shouldCopy = TRUE; } break; case SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE: if (featureCode == StartingFeature) { shouldCopy = TRUE; } break; default: break; } } if (shouldCopy != FALSE) { copyLength = min(featureLength, bytesRemaining); copyLength = min(copyLength, OutputBufferSize - transferedLength); RtlMoveMemory((PUCHAR) OutputBuffer + transferedLength, buffer, copyLength); transferedLength += copyLength; requiredLength += featureLength; } buffer += min(featureLength, bytesRemaining); bytesRemaining -= min(featureLength, bytesRemaining); } } // Adjust Data Length field in header if (transferedLength >= RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength)) { headerLength = (ULONG) requiredLength - RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength); header = (PGET_CONFIGURATION_HEADER) OutputBuffer; REVERSE_BYTES(header->DataLength, &headerLength); } *DataLength = transferedLength; return; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleGetConfiguration( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_GET_CONFIGURATION Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PGET_CONFIGURATION_IOCTL_INPUT inputBuffer = NULL; VOID* outputBuffer = NULL; size_t transferByteCount = 0; PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo; BOOLEAN inZeroPowerState = FALSE; PAGED_CODE (); *DataLength = 0; // if (!cdData->Mmc.IsMmc) { status = STATUS_INVALID_DEVICE_REQUEST; } else { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } if (NT_SUCCESS(status)) { // If device is Zero Power state, there should be no media in device, thus we can synthesize the response // from our cache. Avoid waking up the device in this case. if ((zpoddInfo != NULL) && (zpoddInfo->InZeroPowerState != FALSE)) { inZeroPowerState = TRUE; } if ((inZeroPowerState == FALSE) || (zpoddInfo->GetConfigurationBuffer == NULL)) { CDB cdb; //The maximum number of bytes that a Drive may return //to describe its Features in one GET CONFIGURATION Command is 65,534 transferByteCount = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, (MAXUSHORT - 1)); // If this is a TYPE ONE request and if this device can't handle this // request, then we need to send TYPE ALL request to the device and // convert the data in the completion routine. If required allocate a big // buffer to get both configuration and feature header. if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) && TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG)) { transferByteCount = max(transferByteCount, sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)); } ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION; cdb.GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(transferByteCount >> 8); cdb.GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(transferByteCount & 0xff); cdb.GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(inputBuffer->Feature >> 8); cdb.GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(inputBuffer->Feature & 0xff); cdb.GET_CONFIGURATION.RequestType = (UCHAR)(inputBuffer->RequestType); // If the device does not support TYPE ONE get configuration commands // then change the request type to TYPE ALL. Convert the returned data to // TYPE ONE format in the completion routine. if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) && TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG)) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "DeviceHandleGetConfiguration: Changing TYPE_ONE Get Config to TYPE_ALL\n")); cdb.GET_CONFIGURATION.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL; } status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, (ULONG)transferByteCount, TRUE, &cdb, 12); if (NT_SUCCESS(status)) { if ((inputBuffer->RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) && TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG)) { if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < sizeof(GET_CONFIGURATION_HEADER)) { // Not enough data to calculate the data length. // So assume feature is not present TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DeviceHandleGetConfiguration: No get config header!\n")); *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; } else { //Some CDROM devices do not handle the GET CONFIGURATION commands with //TYPE ONE request. The command will time out causing a bus reset. //To avoid this problem we set a device flag during start device if the device //fails a TYPE ONE request. If this flag is set the TYPE ONE requests will be //tried as TYPE ALL request and the data will be converted to TYPE ONE format //in this routine. GetConfigurationDataConversionTypeAllToTypeOne(inputBuffer->Feature, DeviceExtension->ScratchContext.ScratchSrb, DataLength); *DataLength = min(*DataLength, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength); } } else { *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength; } // copy data to output buffer if (NT_SUCCESS(status)) { RtlMoveMemory(outputBuffer, DeviceExtension->ScratchContext.ScratchBuffer, *DataLength); } } ScratchBuffer_EndUse(DeviceExtension); } else { // We are in Zero Power state, and our cached response is available. // Synthesize the requested data. GetConfigurationDataSynthesize(zpoddInfo->GetConfigurationBuffer, zpoddInfo->GetConfigurationBufferSize, outputBuffer, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, inputBuffer->Feature, inputBuffer->RequestType, DataLength ); } } return status; } _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS RequestHandleGetDriveGeometry( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DISK_GET_LENGTH_INFO IOCTL_DISK_GET_DRIVE_GEOMETRY IOCTL_DISK_GET_DRIVE_GEOMETRY_EX IOCTL_CDROM_GET_DRIVE_GEOMETRY IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX IOCTL_STORAGE_READ_CAPACITY Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; VOID* outputBuffer = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); // Issue ReadCapacity to update device extension // with information for current media. if (NT_SUCCESS(status)) { status = MediaReadCapacity(DeviceExtension->Device); } if (NT_SUCCESS(status)) { switch(RequestParameters.Parameters.DeviceIoControl.IoControlCode) { case IOCTL_DISK_GET_LENGTH_INFO: { PGET_LENGTH_INFORMATION lengthInfo = (PGET_LENGTH_INFORMATION)outputBuffer; lengthInfo->Length = DeviceExtension->PartitionLength; *DataLength = sizeof(GET_LENGTH_INFORMATION); break; } case IOCTL_DISK_GET_DRIVE_GEOMETRY: case IOCTL_CDROM_GET_DRIVE_GEOMETRY: { PDISK_GEOMETRY geometry = (PDISK_GEOMETRY)outputBuffer; *geometry = DeviceExtension->DiskGeometry; *DataLength = sizeof(DISK_GEOMETRY); break; } case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: { PDISK_GEOMETRY_EX geometryEx = (PDISK_GEOMETRY_EX)outputBuffer; geometryEx->DiskSize = DeviceExtension->PartitionLength; geometryEx->Geometry = DeviceExtension->DiskGeometry; *DataLength = FIELD_OFFSET(DISK_GEOMETRY_EX, Data); break; } case IOCTL_STORAGE_READ_CAPACITY: { PSTORAGE_READ_CAPACITY readCapacity = (PSTORAGE_READ_CAPACITY)outputBuffer; readCapacity->Version = sizeof(STORAGE_READ_CAPACITY); readCapacity->Size = sizeof(STORAGE_READ_CAPACITY); readCapacity->BlockLength = DeviceExtension->DiskGeometry.BytesPerSector; if (readCapacity->BlockLength > 0) { readCapacity->NumberOfBlocks.QuadPart = DeviceExtension->PartitionLength.QuadPart/readCapacity->BlockLength; } else { readCapacity->NumberOfBlocks.QuadPart = 0; } readCapacity->DiskLength = DeviceExtension->PartitionLength; *DataLength = sizeof(STORAGE_READ_CAPACITY); break; } default: { NT_ASSERT(FALSE); break; } } // end of switch() } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleDiskVerify( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DISK_VERIFY Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PVERIFY_INFORMATION verifyInfo = NULL; PAGED_CODE (); *DataLength = 0; if (!cdData->Mmc.WriteAllowed) { status = STATUS_MEDIA_WRITE_PROTECTED; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &verifyInfo, NULL); } // handle the request if (NT_SUCCESS(status)) { LARGE_INTEGER byteOffset = {0}; // Add disk offset to starting sector. byteOffset.QuadPart = DeviceExtension->StartingOffset.QuadPart + verifyInfo->StartingOffset.QuadPart; // prevent overflow returning success but only validating small area if (((DeviceExtension->StartingOffset.QuadPart + verifyInfo->StartingOffset.QuadPart) < DeviceExtension->StartingOffset.QuadPart) || ((verifyInfo->Length >> DeviceExtension->SectorShift) > MAXUSHORT) || ((byteOffset.QuadPart >> DeviceExtension->SectorShift) > MAXULONG) ) { status = STATUS_INVALID_PARAMETER; } else { ULONG transferSize = 0; ULONG timeoutValue = 0; CDB cdb; ULONG sectorOffset; USHORT sectorCount; ScratchBuffer_BeginUse(DeviceExtension); // Convert byte offset to sector offset. sectorOffset = (ULONG)(byteOffset.QuadPart >> DeviceExtension->SectorShift); // Convert ULONG byte count to USHORT sector count. sectorCount = (USHORT)(verifyInfo->Length >> DeviceExtension->SectorShift); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.CDB10.OperationCode = SCSIOP_VERIFY; // Move little endian values into CDB in big endian format. cdb.CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)§orOffset)->Byte3; cdb.CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)§orOffset)->Byte2; cdb.CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)§orOffset)->Byte1; cdb.CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)§orOffset)->Byte0; cdb.CDB10.TransferBlocksMsb = ((PFOUR_BYTE)§orCount)->Byte1; cdb.CDB10.TransferBlocksLsb = ((PFOUR_BYTE)§orCount)->Byte0; // The verify command is used by the NT FORMAT utility and // requests are sent down for 5% of the volume size. The // request timeout value is calculated based on the number of // sectors verified. if (sectorCount != 0) { // sectorCount is a USHORT, so no overflow here... timeoutValue = TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, ((sectorCount + 128) / 128)); } status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, transferSize, FALSE, &cdb, 10, timeoutValue); // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleCheckVerify( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_CHECK_VERIFY2 IOCTL_STORAGE_CHECK_VERIFY Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PAGED_CODE (); *DataLength = 0; if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, transferSize, FALSE, &cdb, 6, CDROM_TEST_UNIT_READY_TIMEOUT); if (NT_SUCCESS(status)) { if((RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_CHECK_VERIFY) && (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength)) { PULONG outputBuffer = NULL; status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); if (outputBuffer != NULL) { *outputBuffer = DeviceExtension->MediaChangeCount; *DataLength = sizeof(ULONG); } } else { *DataLength = 0; } } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleFakePartitionInfo( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DISK_GET_DRIVE_LAYOUT IOCTL_DISK_GET_DRIVE_LAYOUT_EX IOCTL_DISK_GET_PARTITION_INFO IOCTL_DISK_GET_PARTITION_INFO_EX Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; VOID* outputBuffer = NULL; ULONG ioctl = RequestParameters.Parameters.DeviceIoControl.IoControlCode; PAGED_CODE (); *DataLength = 0; if (NT_SUCCESS(status)) { if ((ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT) && (ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT_EX) && (ioctl != IOCTL_DISK_GET_PARTITION_INFO) && (ioctl != IOCTL_DISK_GET_PARTITION_INFO_EX)) { status = STATUS_INTERNAL_ERROR; } } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } // handle the request if (NT_SUCCESS(status)) { switch (ioctl) { case IOCTL_DISK_GET_DRIVE_LAYOUT: *DataLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]); RtlZeroMemory(outputBuffer, *DataLength); break; case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: *DataLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]); RtlZeroMemory(outputBuffer, *DataLength); break; case IOCTL_DISK_GET_PARTITION_INFO: *DataLength = sizeof(PARTITION_INFORMATION); RtlZeroMemory(outputBuffer, *DataLength); break; case IOCTL_DISK_GET_PARTITION_INFO_EX: *DataLength = sizeof(PARTITION_INFORMATION_EX); RtlZeroMemory(outputBuffer, *DataLength); break; default: NT_ASSERT(!"Invalid ioctl should not have reached this point\n"); break; } // if we are getting the drive layout, then we need to start by // adding some of the non-partition stuff that says we have // exactly one partition available. if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT) { PDRIVE_LAYOUT_INFORMATION layout; layout = (PDRIVE_LAYOUT_INFORMATION)outputBuffer; layout->PartitionCount = 1; layout->Signature = 1; outputBuffer = (PVOID)(layout->PartitionEntry); ioctl = IOCTL_DISK_GET_PARTITION_INFO; } else if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT_EX) { PDRIVE_LAYOUT_INFORMATION_EX layoutEx; layoutEx = (PDRIVE_LAYOUT_INFORMATION_EX)outputBuffer; layoutEx->PartitionStyle = PARTITION_STYLE_MBR; layoutEx->PartitionCount = 1; layoutEx->Mbr.Signature = 1; outputBuffer = (PVOID)(layoutEx->PartitionEntry); ioctl = IOCTL_DISK_GET_PARTITION_INFO_EX; } // NOTE: the local var 'ioctl' is now modified to either EX or // non-EX version. the local var 'systemBuffer' is now pointing // to the partition information structure. if (ioctl == IOCTL_DISK_GET_PARTITION_INFO) { PPARTITION_INFORMATION partitionInfo; partitionInfo = (PPARTITION_INFORMATION)outputBuffer; partitionInfo->RewritePartition = FALSE; partitionInfo->RecognizedPartition = TRUE; partitionInfo->PartitionType = PARTITION_FAT32; partitionInfo->BootIndicator = FALSE; partitionInfo->HiddenSectors = 0; partitionInfo->StartingOffset.QuadPart = 0; partitionInfo->PartitionLength = DeviceExtension->PartitionLength; partitionInfo->PartitionNumber = 0; } else { PPARTITION_INFORMATION_EX partitionInfo; partitionInfo = (PPARTITION_INFORMATION_EX)outputBuffer; partitionInfo->PartitionStyle = PARTITION_STYLE_MBR; partitionInfo->RewritePartition = FALSE; partitionInfo->Mbr.RecognizedPartition = TRUE; partitionInfo->Mbr.PartitionType = PARTITION_FAT32; partitionInfo->Mbr.BootIndicator = FALSE; partitionInfo->Mbr.HiddenSectors = 0; partitionInfo->StartingOffset.QuadPart = 0; partitionInfo->PartitionLength = DeviceExtension->PartitionLength; partitionInfo->PartitionNumber = 0; } } return status; } NTSTATUS RequestHandleGetDeviceNumber( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_GET_DEVICE_NUMBER Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; *DataLength = 0; if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >= sizeof(STORAGE_DEVICE_NUMBER)) { PSTORAGE_DEVICE_NUMBER deviceNumber = NULL; status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &deviceNumber, NULL); if (NT_SUCCESS(status)) { deviceNumber->DeviceType = DeviceExtension->DeviceObject->DeviceType; deviceNumber->DeviceNumber = DeviceExtension->DeviceNumber; deviceNumber->PartitionNumber = (ULONG)-1; // legacy reason, return (-1) for this IOCTL. status = STATUS_SUCCESS; *DataLength = sizeof(STORAGE_DEVICE_NUMBER); } } else { status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(STORAGE_DEVICE_NUMBER); } return status; } NTSTATUS RequestHandleGetHotPlugInfo( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_GET_HOTPLUG_INFO Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; *DataLength = 0; if(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >= sizeof(STORAGE_HOTPLUG_INFO)) { PSTORAGE_HOTPLUG_INFO info = NULL; status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &info, NULL); if (NT_SUCCESS(status)) { *info = DeviceExtension->PrivateFdoData->HotplugInfo; status = STATUS_SUCCESS; *DataLength = sizeof(STORAGE_HOTPLUG_INFO); } } else { status = STATUS_BUFFER_TOO_SMALL; *DataLength = sizeof(STORAGE_HOTPLUG_INFO); } return status; } NTSTATUS RequestHandleSetHotPlugInfo( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_SET_HOTPLUG_INFO Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PSTORAGE_HOTPLUG_INFO info = NULL; *DataLength = 0; if (RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_HOTPLUG_INFO)) { // Indicate unsuccessful status and no data transferred. status = STATUS_INFO_LENGTH_MISMATCH; } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &info, NULL); } if (NT_SUCCESS(status)) { if (info->Size != DeviceExtension->PrivateFdoData->HotplugInfo.Size) { status = STATUS_INVALID_PARAMETER_1; } if (info->MediaRemovable != DeviceExtension->PrivateFdoData->HotplugInfo.MediaRemovable) { status = STATUS_INVALID_PARAMETER_2; } if (info->MediaHotplug != DeviceExtension->PrivateFdoData->HotplugInfo.MediaHotplug) { status = STATUS_INVALID_PARAMETER_3; } if (info->WriteCacheEnableOverride != DeviceExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride) { status = STATUS_INVALID_PARAMETER_5; } } if (NT_SUCCESS(status)) { DeviceExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug; // Store the user-defined override in the registry DeviceSetParameter(DeviceExtension, CLASSP_REG_SUBKEY_NAME, CLASSP_REG_REMOVAL_POLICY_VALUE_NAME, (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleEventNotification( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_opt_ WDFREQUEST Request, _In_opt_ PWDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: This routine handles the process of IOCTL_STORAGE_EVENT_NOTIFICATION Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - data transferred Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PMEDIA_CHANGE_DETECTION_INFO info = NULL; LONG requestInUse; PSTORAGE_EVENT_NOTIFICATION eventBuffer = NULL; *DataLength = 0; info = DeviceExtension->MediaChangeDetectionInfo; // Since AN is ASYNCHRONOUS and can happen at any time, // make certain not to do anything before properly initialized. if ((!DeviceExtension->IsInitialized) || (info == NULL)) { status = STATUS_UNSUCCESSFUL; } if (NT_SUCCESS(status) && (Request != NULL) && (RequestParameters != NULL)) { // // Validate IOCTL parameters // if (RequestParameters->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_EVENT_NOTIFICATION)) { status = STATUS_INFO_LENGTH_MISMATCH; } // // Check for an supported event // if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters->Parameters.DeviceIoControl.InputBufferLength, &eventBuffer, NULL); if (NT_SUCCESS(status)) { if ((eventBuffer->Version != STORAGE_EVENT_NOTIFICATION_VERSION_V1) || (eventBuffer->Size != sizeof(STORAGE_EVENT_NOTIFICATION))) { status = STATUS_INVALID_PARAMETER; } else if ((eventBuffer->Events & (STORAGE_EVENT_MEDIA_STATUS | STORAGE_EVENT_DEVICE_STATUS | STORAGE_EVENT_DEVICE_OPERATION)) == 0) { status = STATUS_NOT_SUPPORTED; } } } } if (NT_SUCCESS(status)) { if (info->MediaChangeDetectionDisableCount != 0) { TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "RequestHandleEventNotification: device %p has detection disabled \n", DeviceExtension->DeviceObject)); status = STATUS_UNSUCCESSFUL; } } if (NT_SUCCESS(status)) { // if the request is not in use, mark it as such. requestInUse = InterlockedCompareExchange((PLONG)&info->MediaChangeRequestInUse, 1, 0); if (requestInUse != 0) { status = STATUS_UNSUCCESSFUL; } } if (NT_SUCCESS(status)) { // The last MCN finished. ok to issue the new one. RequestSetupMcnSyncIrp(DeviceExtension); // The irp will go into KMDF framework and a request will be created there to represent it. IoCallDriver(DeviceExtension->DeviceObject, info->MediaChangeSyncIrp); } return status; } _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS RequestHandleEjectionControl( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_MEDIA_REMOVAL IOCTL_STORAGE_EJECTION_CONTROL Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PPREVENT_MEDIA_REMOVAL mediaRemoval = NULL; PAGED_CODE (); *DataLength = 0; if(RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(PREVENT_MEDIA_REMOVAL)) { status = STATUS_INFO_LENGTH_MISMATCH; } else { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &mediaRemoval, NULL); } if (NT_SUCCESS(status)) { status = PerformEjectionControl(DeviceExtension, Request, ((RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock : SimpleMediaLock), mediaRemoval->PreventMediaRemoval); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleEnableStreaming( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _Out_ size_t * DataLength ) /*++ Routine Description: Handles an IOCTL_CDROM_ENABLE_STREAMING request Arguments: DeviceExtension - device context Request - request to be handled DataLength - transferred data length Notes: This IOCTL is serialized because it changes read/write behavior and we want to make sure that all previous reads/writes have been completed before we change the behavior. Return Value: NTSTATUS --*/ { PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData; WDFFILEOBJECT fileObject = NULL; PFILE_OBJECT_CONTEXT fileObjectContext = NULL; NTSTATUS status = STATUS_SUCCESS; PCDROM_STREAMING_CONTROL inputBuffer = NULL; BOOLEAN enforceStreamingRead = FALSE; BOOLEAN enforceStreamingWrite = FALSE; BOOLEAN streamingReadSupported, streamingWriteSupported; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, sizeof(CDROM_STREAMING_CONTROL), &inputBuffer, NULL); if (NT_SUCCESS(status)) { // get file object context fileObject = WdfRequestGetFileObject(Request); if (fileObject != NULL) { fileObjectContext = FileObjectGetContext(fileObject); } NT_ASSERT(fileObjectContext != NULL); if (fileObjectContext == NULL) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestHandleEnableStreaming: cannot find file object context\n")); status = STATUS_INVALID_HANDLE; } } if (NT_SUCCESS(status)) { if (inputBuffer->RequestType == CdromStreamingDisable) { enforceStreamingRead = FALSE; enforceStreamingWrite = FALSE; } else if (inputBuffer->RequestType == CdromStreamingEnableForReadOnly) { enforceStreamingRead = TRUE; enforceStreamingWrite = FALSE; } else if (inputBuffer->RequestType == CdromStreamingEnableForWriteOnly) { enforceStreamingRead = FALSE; enforceStreamingWrite = TRUE; } else if (inputBuffer->RequestType == CdromStreamingEnableForReadWrite) { enforceStreamingRead = TRUE; enforceStreamingWrite = TRUE; } streamingReadSupported = cdData->Mmc.StreamingReadSupported && !TEST_FLAG(fdoData->HackFlags, FDO_HACK_NO_STREAMING); streamingWriteSupported = cdData->Mmc.StreamingWriteSupported && !TEST_FLAG(fdoData->HackFlags, FDO_HACK_NO_STREAMING); if ((enforceStreamingRead && !streamingReadSupported) || (enforceStreamingWrite && !streamingWriteSupported)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "RequestHandleEnableStreaming: requested Streaming mode is not supported\n")); status = STATUS_INVALID_DEVICE_REQUEST; } else { fileObjectContext->EnforceStreamingRead = enforceStreamingRead; fileObjectContext->EnforceStreamingWrite = enforceStreamingWrite; } } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleSendOpcInformation( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _Out_ size_t * DataLength ) /*++ Routine Description: Handles an IOCTL_CDROM_SEND_OPC_INFORMATION request Arguments: DeviceExtension - device context Request - request to be handled DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_SIMPLE_OPC_INFO inputBuffer = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, sizeof(CDROM_SIMPLE_OPC_INFO), (PVOID*)&inputBuffer, NULL); if (NT_SUCCESS(status)) { CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // we support only the simplest version for now cdb.SEND_OPC_INFORMATION.OperationCode = SCSIOP_SEND_OPC_INFORMATION; cdb.SEND_OPC_INFORMATION.DoOpc = 1; cdb.SEND_OPC_INFORMATION.Exclude0 = (inputBuffer->Exclude0 ? 1 : 0); cdb.SEND_OPC_INFORMATION.Exclude1 = (inputBuffer->Exclude1 ? 1 : 0); status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, 0, FALSE, &cdb, sizeof(cdb.SEND_OPC_INFORMATION)); // nothing to do after the command finishes ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleGetPerformance( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handles an IOCTL_CDROM_GET_PERFORMANCE request Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_PERFORMANCE_REQUEST inputBuffer = NULL; PVOID outputBuffer = NULL; PAGED_CODE (); *DataLength = 0; // Retrieve pointers to input/output data. The size has been validated earlier // in RequestValidateGetPerformance, so we do not check it again. if (NT_SUCCESS(status)) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&inputBuffer, NULL); } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } if (NT_SUCCESS(status)) { USHORT descriptorSize = 0; USHORT descriptorCount = 0; size_t transferSize = 0; CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); // Set up the CDB RtlZeroMemory(&cdb, sizeof(CDB)); if (inputBuffer->RequestType == CdromPerformanceRequest) { cdb.GET_PERFORMANCE.Type = 0; // 10b is the only defined tolerance in MMCr6 cdb.GET_PERFORMANCE.Tolerance = 2; switch (inputBuffer->Exceptions) { case CdromNominalPerformance: cdb.GET_PERFORMANCE.Except = 0; descriptorSize = sizeof(CDROM_NOMINAL_PERFORMANCE_DESCRIPTOR); break; case CdromEntirePerformanceList: cdb.GET_PERFORMANCE.Except = 1; descriptorSize = sizeof(CDROM_NOMINAL_PERFORMANCE_DESCRIPTOR); break; case CdromPerformanceExceptionsOnly: cdb.GET_PERFORMANCE.Except = 2; descriptorSize = sizeof(CDROM_EXCEPTION_PERFORMANCE_DESCRIPTOR); break; } switch (inputBuffer->PerformanceType) { case CdromReadPerformance: cdb.GET_PERFORMANCE.Write = 0; break; case CdromWritePerformance: cdb.GET_PERFORMANCE.Write = 1; break; } REVERSE_BYTES(&cdb.GET_PERFORMANCE.StartingLBA, &inputBuffer->StaringLba); } else if (inputBuffer->RequestType == CdromWriteSpeedRequest) { cdb.GET_PERFORMANCE.Type = 3; descriptorSize = sizeof(CDROM_WRITE_SPEED_DESCRIPTOR); } cdb.GET_PERFORMANCE.OperationCode = SCSIOP_GET_PERFORMANCE; // calculate how many descriptors can fit into the output buffer if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength >= sizeof(CDROM_PERFORMANCE_HEADER) && RequestParameters.Parameters.DeviceIoControl.OutputBufferLength <= MAXUSHORT && descriptorSize > 0) { descriptorCount = (USHORT)(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength - sizeof(CDROM_PERFORMANCE_HEADER)); descriptorCount /= descriptorSize; } else { status = STATUS_INVALID_PARAMETER; } REVERSE_BYTES_SHORT(&cdb.GET_PERFORMANCE.MaximumNumberOfDescriptors, &descriptorCount); // Calculate transfer size. We round it up to meet adapter requirements. // Extra bytes are discarded later, when we copy data to the output buffer. transferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength; transferSize += DeviceExtension->AdapterDescriptor->AlignmentMask; transferSize &= ~DeviceExtension->AdapterDescriptor->AlignmentMask; if (NT_SUCCESS(status)) { status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, Request, (ULONG)transferSize, TRUE, &cdb, sizeof(cdb.GET_PERFORMANCE), CDROM_GET_PERFORMANCE_TIMEOUT); } if (NT_SUCCESS(status)) { if (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength < sizeof(CDROM_PERFORMANCE_HEADER)) { *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; } else { *DataLength = min(RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength); RtlCopyMemory(outputBuffer, DeviceExtension->ScratchContext.ScratchBuffer, *DataLength); } } ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleMcnSyncFakeIoctl( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _Out_ size_t * DataLength ) /*++ Routine Description: Handles an IOCTL_MCN_SYNC_FAKE_IOCTL request Arguments: DeviceExtension - device context DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; BOOLEAN shouldRetry = TRUE; BOOLEAN requestSent = FALSE; PAGED_CODE (); *DataLength = 0; // // Try to acquire the media change event. If we can't do it immediately // then bail out and assume the caller will try again later. // while (shouldRetry) { status = RequestSetupMcnRequest(DeviceExtension, info->Gesn.Supported); if (!NT_SUCCESS(status)) { shouldRetry = FALSE; } if (NT_SUCCESS(status)) { requestSent = RequestSendMcnRequest(DeviceExtension); if (requestSent) { shouldRetry = RequestPostWorkMcnRequest(DeviceExtension); } else { shouldRetry = FALSE; } } } // If there were any media change notifications that were not delivered // for some reason, make an attempt to do so at this time. DeviceSendDelayedMediaChangeNotifications(DeviceExtension); // Set the status and then complete the original request. // The timer handler will be able to send the next request. status = STATUS_SUCCESS; return status; } BOOLEAN RequestIsRealtimeStreaming( _In_ WDFREQUEST Request, _In_ BOOLEAN IsReadRequest ) /*++ Routine Description: Checks whether a given read/write request should be performed in Real-Time Streaming mode. Arguments: Request - request to be checked IsReadRequest - TRUE = read request; FALSE = write request Return Value: TRUE - a Real-Time Streaming operation has to be performed FALSE - a normal (non-Streaming) operation has to be performed --*/ { BOOLEAN useStreaming = FALSE; if (!useStreaming) { // // Check if we're required to use Streaming via I/O Stack Location flags // UCHAR currentStackLocationFlags = 0; currentStackLocationFlags = RequestGetCurrentStackLocationFlags(Request); useStreaming = TEST_FLAG(currentStackLocationFlags, SL_REALTIME_STREAM); } if (!useStreaming) { // // Check if we were previously requested to enforce Streaming for // the file handle through which this request was sent. // WDFFILEOBJECT fileObject; PFILE_OBJECT_CONTEXT fileObjectContext; fileObject = WdfRequestGetFileObject(Request); if (fileObject != NULL) { fileObjectContext = FileObjectGetContext(fileObject); NT_ASSERT(fileObjectContext != NULL); if (IsReadRequest && fileObjectContext->EnforceStreamingRead) { useStreaming = TRUE; } if (!IsReadRequest && fileObjectContext->EnforceStreamingWrite) { useStreaming = TRUE; } } } return useStreaming; } NTSTATUS RequestValidateReadWrite( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters ) /*++ Routine Description: Validate Read/Write request Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); BOOLEAN isValid = TRUE; LONGLONG startingOffset = 0; size_t transferByteCount = 0; PIRP irp = NULL; PIO_STACK_LOCATION currentStack = NULL; irp = WdfRequestWdmGetIrp(Request); currentStack = IoGetCurrentIrpStackLocation(irp); if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) && (currentStack->MinorFunction != CDROM_VOLUME_VERIFY_CHECKED) && !TEST_FLAG(currentStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { // DO_VERIFY_VOLUME is set for the device object, // but this request is not itself a verify request. // So fail this request. //set the status for volume verification. status = STATUS_VERIFY_REQUIRED; } if (NT_SUCCESS(status)) { if (PLAY_ACTIVE(DeviceExtension)) { status = STATUS_DEVICE_BUSY; } } if (NT_SUCCESS(status)) { // If the device is in exclusive mode, check whether the request is from // the handle that locked the device. if (EXCLUSIVE_MODE(cdData) && !EXCLUSIVE_OWNER(cdData, WdfRequestGetFileObject(Request))) { // This request is not from the owner. We can't let the operation go. TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Access Denied! Device in exclusive mode.\n")); status = STATUS_ACCESS_DENIED; } } // Validate the request alignment. if (NT_SUCCESS(status)) { if (RequestParameters.Type == WdfRequestTypeRead) { startingOffset = RequestParameters.Parameters.Read.DeviceOffset; transferByteCount = RequestParameters.Parameters.Read.Length; } else { startingOffset = RequestParameters.Parameters.Write.DeviceOffset; transferByteCount = RequestParameters.Parameters.Write.Length; } if (!DeviceExtension->DiskGeometry.BytesPerSector) { DeviceExtension->DiskGeometry.BytesPerSector = 2048; } if (!DeviceExtension->SectorShift) { DeviceExtension->SectorShift = 11; } // Perform some basic validation up front if (TEST_FLAG(startingOffset, DeviceExtension->DiskGeometry.BytesPerSector - 1) || TEST_FLAG(transferByteCount, DeviceExtension->DiskGeometry.BytesPerSector - 1)) { status = STATUS_INVALID_DEVICE_REQUEST; } } // validate the request against the current mmc schema if (NT_SUCCESS(status)) { FEATURE_NUMBER schema = cdData->Mmc.ValidationSchema; // We validate read requests according to the RandomWritable schema, except in the // case of IncrementalStreamingWritable, wherein the drive is responsible for all // of the verification if (RequestParameters.Type == WdfRequestTypeRead) { if (!cdData->Mmc.WriteAllowed) { // standard legacy validation of read irps // if writing is not allowed on the media schema = FeatureRandomWritable; } else if (schema != FeatureIncrementalStreamingWritable) { // standard legacy validation of read irps // if not using streaming writes on writable media schema = FeatureRandomWritable; } } // Fail write requests to read-only media if ((RequestParameters.Type == WdfRequestTypeWrite) && !(cdData->Mmc.WriteAllowed)) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Write request to read-only media\n")); isValid = FALSE; } if (isValid) { switch (schema) { case FeatureDefectManagement: case FeatureRandomWritable: // Ensure that the request is within bounds for ROM drives. // Writer drives do not need to have bounds as outbounds request should not damage the drive. if(!cdData->Mmc.IsWriter) { if ((startingOffset >= DeviceExtension->PartitionLength.QuadPart) || startingOffset < 0) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Request is out of bounds\n")); isValid = FALSE; } else { ULONGLONG bytesRemaining = DeviceExtension->PartitionLength.QuadPart - startingOffset; if ((ULONGLONG)transferByteCount > bytesRemaining) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Request is out of bounds\n")); isValid = FALSE; } } } break; case FeatureRigidRestrictedOverwrite: // Ensure that the number of blocks is a multiple of the blocking size if (((transferByteCount >> DeviceExtension->SectorShift) % cdData->Mmc.Blocking) != 0) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Number of blocks is not a multiple of the blocking size (%x)\n", cdData->Mmc.Blocking)); isValid = FALSE; } // Fall through case FeatureRestrictedOverwrite: // Ensure that the request begins on a blocking boundary if ((Int64ShrlMod32(startingOffset, DeviceExtension->SectorShift) % cdData->Mmc.Blocking) != 0) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Starting block is not a multiple of the blocking size (%x)\n", cdData->Mmc.Blocking)); isValid = FALSE; } break; case FeatureIncrementalStreamingWritable: // Let the drive handle the verification break; default: // Unknown schema. Fail the request TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Unknown validation schema (%x)\n", schema)); isValid = FALSE; break; } //end of switch (schema) } // end of if (isValid) if (!isValid) { status = STATUS_INVALID_DEVICE_REQUEST; } } // end of mmc schema validation // validate that the Real-Time Streaming requests meet device capabilties if (NT_SUCCESS(status)) { // We do not check for FDO_HACK_NO_STREAMING in DeviceExtension->PrivateFdoData->HackFlags here, // because we're going to hide device failures related to streaming reads/writes from the sender // of the request. FDO_HACK_NO_STREAMING is going to be taken into account later during actual // processing of the request. if (RequestIsRealtimeStreaming(Request, RequestParameters.Type == WdfRequestTypeRead)) { if (RequestParameters.Type == WdfRequestTypeRead && !cdData->Mmc.StreamingReadSupported) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Streaming reads are not supported.\n")); status = STATUS_INVALID_DEVICE_REQUEST; } if (RequestParameters.Type == WdfRequestTypeWrite && !cdData->Mmc.StreamingWriteSupported) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "RequestValidateReadWrite: Streaming writes are not supported.\n")); status = STATUS_INVALID_DEVICE_REQUEST; } } } return status; } NTSTATUS RequestHandleReadWrite( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters ) /*++ Routine Description: Handle a read/write request Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); size_t transferByteCount = 0; PIRP irp = NULL; PIO_STACK_LOCATION currentStack = NULL; PUCHAR dataBuffer; irp = WdfRequestWdmGetIrp(Request); currentStack = IoGetCurrentIrpStackLocation(irp); dataBuffer = MmGetMdlVirtualAddress(irp->MdlAddress); if (NT_SUCCESS(status)) { if (RequestParameters.Type == WdfRequestTypeRead) { transferByteCount = RequestParameters.Parameters.Read.Length; } else { transferByteCount = RequestParameters.Parameters.Write.Length; } if (transferByteCount == 0) { // Several parts of the code turn 0 into 0xffffffff, // so don't process a zero-length request any further. status = STATUS_SUCCESS; RequestCompletion(DeviceExtension, Request, status, 0); return status; } // Add partition byte offset to make starting byte relative to // beginning of disk. currentStack->Parameters.Read.ByteOffset.QuadPart += (DeviceExtension->StartingOffset.QuadPart); //not very necessary as the starting offset for CD/DVD device is always 0. if (RequestParameters.Type == WdfRequestTypeRead) { RequestParameters.Parameters.Read.DeviceOffset = currentStack->Parameters.Read.ByteOffset.QuadPart; } else { RequestParameters.Parameters.Write.DeviceOffset = currentStack->Parameters.Write.ByteOffset.QuadPart; } } if (NT_SUCCESS(status)) { ULONG entireXferLen = currentStack->Parameters.Read.Length; ULONG maxLength = 0; ULONG packetsCount = 0; PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext; PCDROM_REQUEST_CONTEXT requestContext; PCDROM_REQUEST_CONTEXT originalRequestContext; // get the count of packets we need to send. if ((((ULONG_PTR)dataBuffer) & (PAGE_SIZE-1)) == 0) { maxLength = cdData->MaxPageAlignedTransferBytes; } else { maxLength = cdData->MaxUnalignedTransferBytes; } packetsCount = entireXferLen / maxLength; if (entireXferLen % maxLength != 0) { packetsCount++; } originalRequestContext = RequestGetContext(Request); ScratchBuffer_BeginUse(DeviceExtension); readWriteContext = &DeviceExtension->ScratchContext.ScratchReadWriteContext; requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest); readWriteContext->PacketsCount = packetsCount; readWriteContext->EntireXferLen = entireXferLen; readWriteContext->MaxLength = maxLength; readWriteContext->StartingOffset = currentStack->Parameters.Read.ByteOffset; readWriteContext->DataBuffer = dataBuffer; readWriteContext->TransferedBytes = 0; readWriteContext->IsRead = (RequestParameters.Type == WdfRequestTypeRead); requestContext->OriginalRequest = Request; requestContext->DeviceExtension = DeviceExtension; // // Setup the READ/WRITE fields in the original request which is what // we use to properly synchronize cancellation logic between the // cancel callback and the timer routine. // originalRequestContext->ReadWriteIsCompleted = FALSE; originalRequestContext->ReadWriteRetryInitialized = FALSE; originalRequestContext->DeviceExtension = DeviceExtension; status = ScratchBuffer_PerformNextReadWrite(DeviceExtension, TRUE); // We do not call ScratchBuffer_EndUse here, because we're not releasing the scratch SRB. // It will be released in the completion routine. } return status; } _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS RequestHandleLoadEjectMedia( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_EJECT_MEDIA IOCTL_STORAGE_LOAD_MEDIA IOCTL_STORAGE_LOAD_MEDIA2 Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo; PAGED_CODE (); *DataLength = 0; if (NT_SUCCESS(status)) { // Synchronize with ejection control and ejection cleanup code as // well as other eject/load requests. WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL); if(DeviceExtension->ProtectedLockCount != 0) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "RequestHandleLoadEjectMedia: call to eject protected locked " "device - failure\n")); status = STATUS_DEVICE_BUSY; } if (NT_SUCCESS(status)) { SCSI_REQUEST_BLOCK srb; PCDB cdb = (PCDB)srb.Cdb; RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); srb.CdbLength = 6; cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; cdb->START_STOP.LoadEject = 1; if(RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_EJECT_MEDIA) { cdb->START_STOP.Start = 0; // We are sending down a soft eject, and in this case we should take an active ref // if the command succeeds. if ((zpoddInfo != NULL) && (zpoddInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) && (zpoddInfo->Load == 0)) // Drawer { zpoddInfo->MonitorStartStopUnit = TRUE; } } else { cdb->START_STOP.Start = 1; } status = DeviceSendSrbSynchronously(DeviceExtension->Device, &srb, NULL, 0, FALSE, Request); if (zpoddInfo != NULL) { zpoddInfo->MonitorStartStopUnit = FALSE; } } WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock); } return status; } _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS RequestHandleReserveRelease( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_RESERVE IOCTL_STORAGE_RELEASE Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PSCSI_REQUEST_BLOCK srb = NULL; PCDB cdb = NULL; ULONG ioctlCode = 0; PAGED_CODE (); *DataLength = 0; srb = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK) + (sizeof(ULONG_PTR) * 2), CDROM_TAG_SRB); if (srb == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(status)) { ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode; cdb = (PCDB)srb->Cdb; RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6)) { srb->CdbLength = 10; cdb->CDB10.OperationCode = (ioctlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10; } else { srb->CdbLength = 6; cdb->CDB6GENERIC.OperationCode = (ioctlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT : SCSIOP_RELEASE_UNIT; } // Set timeout value. srb->TimeOutValue = DeviceExtension->TimeOutValue; // Send reserves as tagged requests. if (ioctlCode == IOCTL_STORAGE_RESERVE) { SET_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; } status = DeviceSendSrbSynchronously(DeviceExtension->Device, srb, NULL, 0, FALSE, Request); // no data transfer. *DataLength = 0; FREE_POOL(srb); } return status; } // end RequestHandleReserveRelease() static // __REACTOS__ BOOLEAN ValidPersistentReserveScope( UCHAR Scope) { switch (Scope) { case RESERVATION_SCOPE_LU: case RESERVATION_SCOPE_ELEMENT: return TRUE; default: return FALSE; } } static // __REACTOS__ BOOLEAN ValidPersistentReserveType( UCHAR Type) { switch (Type) { case RESERVATION_TYPE_WRITE_EXCLUSIVE: case RESERVATION_TYPE_EXCLUSIVE: case RESERVATION_TYPE_WRITE_EXCLUSIVE_REGISTRANTS: case RESERVATION_TYPE_EXCLUSIVE_REGISTRANTS: return TRUE; default: return FALSE; } } NTSTATUS RequestValidatePersistentReserve( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Validate request of IOCTL_STORAGE_PERSISTENT_RESERVE_IN IOCTL_STORAGE_PERSISTENT_RESERVE_OUT Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode; PPERSISTENT_RESERVE_COMMAND reserveCommand = NULL; *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&reserveCommand, NULL); if (NT_SUCCESS(status)) { if ((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < sizeof(PERSISTENT_RESERVE_COMMAND)) || (reserveCommand->Size < sizeof(PERSISTENT_RESERVE_COMMAND))) { *DataLength = 0; status = STATUS_INFO_LENGTH_MISMATCH; } else if ((ULONG_PTR)reserveCommand & DeviceExtension->AdapterDescriptor->AlignmentMask) { // Check buffer alignment. Only an issue if another kernel mode component // (not the I/O manager) allocates the buffer. *DataLength = 0; status = STATUS_INVALID_USER_BUFFER; } } if (NT_SUCCESS(status)) { if (ioctlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN) { if (RequestParameters.Parameters.DeviceIoControl.OutputBufferLength < reserveCommand->PR_IN.AllocationLength) { *DataLength = 0; status = STATUS_INVALID_PARAMETER; } else { switch (reserveCommand->PR_IN.ServiceAction) { case RESERVATION_ACTION_READ_KEYS: if (reserveCommand->PR_IN.AllocationLength < sizeof(PRI_REGISTRATION_LIST)) { *DataLength = 0; status = STATUS_INVALID_PARAMETER; } break; case RESERVATION_ACTION_READ_RESERVATIONS: if (reserveCommand->PR_IN.AllocationLength < sizeof(PRI_RESERVATION_LIST)) { *DataLength = 0; status = STATUS_INVALID_PARAMETER; } break; default: *DataLength = 0; status = STATUS_INVALID_PARAMETER; break; } } } else // case of IOCTL_STORAGE_PERSISTENT_RESERVE_OUT { // Verify ServiceAction, Scope, and Type switch (reserveCommand->PR_OUT.ServiceAction) { case RESERVATION_ACTION_REGISTER: case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING: case RESERVATION_ACTION_CLEAR: // Scope and type ignored. break; case RESERVATION_ACTION_RESERVE: case RESERVATION_ACTION_RELEASE: case RESERVATION_ACTION_PREEMPT: case RESERVATION_ACTION_PREEMPT_ABORT: if (!ValidPersistentReserveScope(reserveCommand->PR_OUT.Scope) || !ValidPersistentReserveType(reserveCommand->PR_OUT.Type)) { *DataLength = 0; status = STATUS_INVALID_PARAMETER; } break; default: *DataLength = 0; status = STATUS_INVALID_PARAMETER; break; } // Check input buffer for PR Out. // Caller must include the PR parameter list. if (NT_SUCCESS(status)) { if ((RequestParameters.Parameters.DeviceIoControl.InputBufferLength < (sizeof(PERSISTENT_RESERVE_COMMAND) + sizeof(PRO_PARAMETER_LIST))) || (reserveCommand->Size < RequestParameters.Parameters.DeviceIoControl.InputBufferLength)) { *DataLength = 0; status = STATUS_INVALID_PARAMETER; } } } // end of validation for IOCTL_STORAGE_PERSISTENT_RESERVE_OUT } return status; } // end RequestValidatePersistentReserve() _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS RequestHandlePersistentReserve( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_PERSISTENT_RESERVE_IN IOCTL_STORAGE_PERSISTENT_RESERVE_OUT Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG ioctlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode; PSCSI_REQUEST_BLOCK srb = NULL; PCDB cdb = NULL; BOOLEAN writeToDevice; PPERSISTENT_RESERVE_COMMAND reserveCommand = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&reserveCommand, NULL); if (NT_SUCCESS(status)) { srb = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK) + (sizeof(ULONG_PTR) * 2), CDROM_TAG_SRB); if (srb == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } else { cdb = (PCDB)srb->Cdb; RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); } } if (NT_SUCCESS(status)) { size_t dataBufLen = 0; // Fill in the CDB. if (ioctlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN) { cdb->PERSISTENT_RESERVE_IN.OperationCode = SCSIOP_PERSISTENT_RESERVE_IN; cdb->PERSISTENT_RESERVE_IN.ServiceAction = reserveCommand->PR_IN.ServiceAction; REVERSE_BYTES_SHORT(&(cdb->PERSISTENT_RESERVE_IN.AllocationLength), &(reserveCommand->PR_IN.AllocationLength)); dataBufLen = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength; writeToDevice = FALSE; } else { cdb->PERSISTENT_RESERVE_OUT.OperationCode = SCSIOP_PERSISTENT_RESERVE_OUT; cdb->PERSISTENT_RESERVE_OUT.ServiceAction = reserveCommand->PR_OUT.ServiceAction; cdb->PERSISTENT_RESERVE_OUT.Scope = reserveCommand->PR_OUT.Scope; cdb->PERSISTENT_RESERVE_OUT.Type = reserveCommand->PR_OUT.Type; cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = (UCHAR)sizeof(PRO_PARAMETER_LIST); // Move the parameter list to the beginning of the data buffer (so it is aligned // correctly and that the MDL describes it correctly). RtlMoveMemory(reserveCommand, reserveCommand->PR_OUT.ParameterList, sizeof(PRO_PARAMETER_LIST)); dataBufLen = sizeof(PRO_PARAMETER_LIST); writeToDevice = TRUE; } srb->CdbLength = 10; srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; status = DeviceSendSrbSynchronously(DeviceExtension->Device, srb, reserveCommand, (ULONG) dataBufLen, writeToDevice, Request); FREE_POOL(srb); } return status; } #if (NTDDI_VERSION >= NTDDI_WIN8) _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleAreVolumesReady( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DISK_ARE_VOLUMES_READY Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { BOOLEAN completeRequest = TRUE; NTSTATUS status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(RequestParameters); *DataLength = 0; if (DeviceExtension->IsVolumeOnlinePending == FALSE) { status = STATUS_SUCCESS; goto Cleanup; } // // Add to the volume ready queue. No worries about request cancellation, // since KMDF will automatically handle that while request is in queue. // status = WdfRequestForwardToIoQueue(Request, DeviceExtension->ManualVolumeReadyQueue ); if(!NT_SUCCESS(status)) { goto Cleanup; } status = STATUS_PENDING; completeRequest = FALSE; Cleanup: if (completeRequest) { RequestCompletion(DeviceExtension, Request, status, 0); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS RequestHandleVolumeOnline( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_VOLUME_ONLINE / IOCTL_VOLUME_POST_ONLINE Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; WDFREQUEST request; PIRP irp = NULL; PIO_STACK_LOCATION nextStack = NULL; UNREFERENCED_PARAMETER(RequestParameters); *DataLength = 0; DeviceExtension->IsVolumeOnlinePending = FALSE; // Complete all parked volume ready requests. for (;;) { status = WdfIoQueueRetrieveNextRequest(DeviceExtension->ManualVolumeReadyQueue, &request); if (!NT_SUCCESS(status)) { break; } RequestCompletion(DeviceExtension, request, STATUS_SUCCESS, 0); } // Change the IOCTL code to IOCTL_DISK_VOLUMES_ARE_READY, and forward the request down, // so that if the underlying port driver will also know that volume is ready. WdfRequestFormatRequestUsingCurrentType(Request); irp = WdfRequestWdmGetIrp(Request); nextStack = IoGetNextIrpStackLocation(irp); irp->AssociatedIrp.SystemBuffer = &DeviceExtension->DeviceNumber; nextStack->Parameters.DeviceIoControl.OutputBufferLength = 0; nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof (DeviceExtension->DeviceNumber); nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_DISK_VOLUMES_ARE_READY; // Send the request straight down (synchronously). RequestSend(DeviceExtension, Request, DeviceExtension->IoTarget, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, NULL); return STATUS_SUCCESS; } #endif _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS DeviceHandleRawRead( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_RAW_READ Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { // Determine whether the drive is currently in raw or cooked mode, // and which command to use to read the data. NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); RAW_READ_INFO rawReadInfo = {0}; PVOID outputVirtAddr = NULL; ULONG startingSector; PIRP irp = WdfRequestWdmGetIrp(Request); PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(irp); VOID* outputBuffer = NULL; PAGED_CODE (); *DataLength = 0; if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME) && (currentIrpStack->MinorFunction != CDROM_VOLUME_VERIFY_CHECKED) && !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { // DO_VERIFY_VOLUME is set for the device object, // but this request is not itself a verify request. // So fail this request. (VOID) MediaReadCapacity(DeviceExtension->Device); *DataLength = 0; //set the status for volume verification. status = STATUS_VERIFY_REQUIRED; } if (NT_SUCCESS(status)) { // Since this ioctl is METHOD_OUT_DIRECT, we need to copy away // the input buffer before interpreting it. This prevents a // malicious app from messing with the input buffer while we // are interpreting it. This is done in dispacth. // // Here, we are going to get the input buffer out of saved place. rawReadInfo = *(PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength > 0) { // Make sure that any user buffer that we pass down to // the hardware is properly aligned NT_ASSERT(irp->MdlAddress); outputVirtAddr = MmGetMdlVirtualAddress(irp->MdlAddress); if ((ULONG_PTR)outputVirtAddr & DeviceExtension->AdapterDescriptor->AlignmentMask) { NT_ASSERT(!((ULONG_PTR)outputVirtAddr & DeviceExtension->AdapterDescriptor->AlignmentMask)); *DataLength = 0; status = STATUS_INVALID_PARAMETER; } } else { status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } if (NT_SUCCESS(status)) { switch (rawReadInfo.TrackMode) { case CDDA: case YellowMode2: case XAForm2: // no check needed. break; case RawWithC2AndSubCode: if (!cdData->Mmc.ReadCdC2Pointers || !cdData->Mmc.ReadCdSubCode) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "Request to read C2 & Subcode rejected. " "Is C2 supported: %d Is Subcode supported: %d\n", cdData->Mmc.ReadCdC2Pointers, cdData->Mmc.ReadCdSubCode )); *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; } break; case RawWithC2: if (!cdData->Mmc.ReadCdC2Pointers) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "Request to read C2 rejected because drive does not " "report support for C2 pointers\n" )); *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; } break; case RawWithSubCode: if (!cdData->Mmc.ReadCdSubCode) { TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "Request to read subcode rejected because drive does " "not report support for reading the subcode data\n" )); *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; } break; default: *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; break; } } if (NT_SUCCESS(status)) { PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb; PCDB cdb = (PCDB)(srb->Cdb); size_t transferByteCount; BOOLEAN shouldRetry = TRUE; ULONG timesAlreadyRetried = 0; LONGLONG retryIn100nsUnits = 0; transferByteCount = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength; ScratchBuffer_BeginUse(DeviceExtension); while (shouldRetry) { // Setup cdb depending upon the sector type we want. switch (rawReadInfo.TrackMode) { case CDDA: transferByteCount = rawReadInfo.SectorCount * RAW_SECTOR_SIZE; cdb->READ_CD.ExpectedSectorType = CD_DA_SECTOR; cdb->READ_CD.IncludeUserData = 1; cdb->READ_CD.HeaderCode = 3; cdb->READ_CD.IncludeSyncData = 1; break; case YellowMode2: transferByteCount = rawReadInfo.SectorCount * RAW_SECTOR_SIZE; cdb->READ_CD.ExpectedSectorType = YELLOW_MODE2_SECTOR; cdb->READ_CD.IncludeUserData = 1; cdb->READ_CD.HeaderCode = 1; cdb->READ_CD.IncludeSyncData = 1; break; case XAForm2: transferByteCount = rawReadInfo.SectorCount * RAW_SECTOR_SIZE; cdb->READ_CD.ExpectedSectorType = FORM2_MODE2_SECTOR; cdb->READ_CD.IncludeUserData = 1; cdb->READ_CD.HeaderCode = 3; cdb->READ_CD.IncludeSyncData = 1; break; case RawWithC2AndSubCode: transferByteCount = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE; cdb->READ_CD.ExpectedSectorType = 0; // Any sector type cdb->READ_CD.IncludeUserData = 1; cdb->READ_CD.HeaderCode = 3; // Header and subheader returned cdb->READ_CD.IncludeSyncData = 1; cdb->READ_CD.ErrorFlags = 2; // C2 and block error cdb->READ_CD.SubChannelSelection = 1; // raw subchannel data break; case RawWithC2: transferByteCount = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_C2_SIZE; cdb->READ_CD.ExpectedSectorType = 0; // Any sector type cdb->READ_CD.IncludeUserData = 1; cdb->READ_CD.HeaderCode = 3; // Header and subheader returned cdb->READ_CD.IncludeSyncData = 1; cdb->READ_CD.ErrorFlags = 2; // C2 and block error break; case RawWithSubCode: transferByteCount = rawReadInfo.SectorCount * CD_RAW_SECTOR_WITH_SUBCODE_SIZE; cdb->READ_CD.ExpectedSectorType = 0; // Any sector type cdb->READ_CD.IncludeUserData = 1; cdb->READ_CD.HeaderCode = 3; // Header and subheader returned cdb->READ_CD.IncludeSyncData = 1; cdb->READ_CD.SubChannelSelection = 1; // raw subchannel data break; default: // should already checked before coming in loop. NT_ASSERT(FALSE); break; } ScratchBuffer_SetupSrb(DeviceExtension, Request, (ULONG)transferByteCount, TRUE); // Restore the buffer, buffer size and MdlAddress. They got changed but we don't want to use the scratch buffer. { PIRP scratchIrp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest); scratchIrp->MdlAddress = irp->MdlAddress; srb->DataBuffer = outputVirtAddr; srb->DataTransferLength = (ULONG)transferByteCount; } // Calculate starting offset. startingSector = (ULONG)(rawReadInfo.DiskOffset.QuadPart >> DeviceExtension->SectorShift); // Fill in CDB fields. srb->CdbLength = 12; cdb->READ_CD.OperationCode = SCSIOP_READ_CD; cdb->READ_CD.TransferBlocks[2] = (UCHAR) (rawReadInfo.SectorCount & 0xFF); cdb->READ_CD.TransferBlocks[1] = (UCHAR) (rawReadInfo.SectorCount >> 8 ); cdb->READ_CD.TransferBlocks[0] = (UCHAR) (rawReadInfo.SectorCount >> 16); cdb->READ_CD.StartingLBA[3] = (UCHAR) (startingSector & 0xFF); cdb->READ_CD.StartingLBA[2] = (UCHAR) ((startingSector >> 8)); cdb->READ_CD.StartingLBA[1] = (UCHAR) ((startingSector >> 16)); cdb->READ_CD.StartingLBA[0] = (UCHAR) ((startingSector >> 24)); ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL); if ((DeviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) && (DeviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED)) { shouldRetry = FALSE; status = STATUS_CANCELLED; } else { shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension, timesAlreadyRetried, &status, &retryIn100nsUnits); if (shouldRetry) { LARGE_INTEGER t; t.QuadPart = -retryIn100nsUnits; timesAlreadyRetried++; KeDelayExecutionThread(KernelMode, FALSE, &t); // keep items clean ScratchBuffer_ResetItems(DeviceExtension, FALSE); } } } if (NT_SUCCESS(status)) { *DataLength = srb->DataTransferLength; } ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandlePlayAudioMsf( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_PLAY_AUDIO_MSF Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_PLAY_AUDIO_MSF inputBuffer = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&inputBuffer, NULL); if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF; cdb.PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM; cdb.PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS; cdb.PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF; cdb.PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM; cdb.PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS; cdb.PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF; status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10); if (NT_SUCCESS(status)) { PLAY_ACTIVE(DeviceExtension) = TRUE; *DataLength = 0; } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleReadQChannel( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_READ_Q_CHANNEL Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PVOID inputBuffer = NULL; PVOID outputBuffer = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } if (NT_SUCCESS(status)) { status = ReadQChannel(DeviceExtension, Request, inputBuffer, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, outputBuffer, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, DataLength); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS ReadQChannel( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_opt_ WDFREQUEST OriginalRequest, _In_ PVOID InputBuffer, _In_ size_t InputBufferLength, _In_ PVOID OutputBuffer, _In_ size_t OutputBufferLength, _Out_ size_t * DataLength ) /*++ Routine Description: base function to handle request of IOCTL_CDROM_READ_Q_CHANNEL Arguments: DeviceExtension - device context OriginalRequest - original request to be handled InputBuffer - input buffer InputBufferLength - length of input buffer OutputBuffer - output buffer OutputBufferLength - length of output buffer Return Value: NTSTATUS DataLength - returned data length --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG transferByteCount = 0; CDB cdb; PCDROM_SUB_Q_DATA_FORMAT inputBuffer = (PCDROM_SUB_Q_DATA_FORMAT)InputBuffer; PSUB_Q_CHANNEL_DATA userChannelData = (PSUB_Q_CHANNEL_DATA)OutputBuffer; PAGED_CODE (); UNREFERENCED_PARAMETER(InputBufferLength); *DataLength = 0; // Set size of channel data -- however, this is dependent on // what information we are requesting (which Format) switch( inputBuffer->Format ) { case IOCTL_CDROM_CURRENT_POSITION: transferByteCount = sizeof(SUB_Q_CURRENT_POSITION); break; case IOCTL_CDROM_MEDIA_CATALOG: transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER); break; case IOCTL_CDROM_TRACK_ISRC: transferByteCount = sizeof(SUB_Q_TRACK_ISRC); break; } ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB // Always logical unit 0, but only use MSF addressing // for IOCTL_CDROM_CURRENT_POSITION if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION) { cdb.SUBCHANNEL.Msf = CDB_USE_MSF; } // Return subchannel data cdb.SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK; // Specify format of informatin to return cdb.SUBCHANNEL.Format = inputBuffer->Format; // Specify which track to access (only used by Track ISRC reads) if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC) { cdb.SUBCHANNEL.TrackNumber = inputBuffer->Track; } cdb.SUBCHANNEL.AllocationLength[0] = (UCHAR) (transferByteCount >> 8); cdb.SUBCHANNEL.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF); cdb.SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL; status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, transferByteCount, TRUE, &cdb, 10); if (NT_SUCCESS(status)) { PSUB_Q_CHANNEL_DATA subQPtr = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer; #if DBG switch( inputBuffer->Format ) { case IOCTL_CDROM_CURRENT_POSITION: TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus )); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR )); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Control = 0x%x\n", subQPtr->CurrentPosition.Control )); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Track = %u\n", subQPtr->CurrentPosition.TrackNumber )); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Index = %u\n", subQPtr->CurrentPosition.IndexNumber )); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) )); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) )); break; case IOCTL_CDROM_MEDIA_CATALOG: TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus )); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Mcval is %u\n", subQPtr->MediaCatalog.Mcval )); break; case IOCTL_CDROM_TRACK_ISRC: TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus )); TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,"ReadQChannel: Tcval is %u\n", subQPtr->TrackIsrc.Tcval )); break; } #endif // Update the play active status. if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) { PLAY_ACTIVE(DeviceExtension) = TRUE; } else { PLAY_ACTIVE(DeviceExtension) = FALSE; } // Check if output buffer is large enough to contain // the data. if (OutputBufferLength < DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength) { DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength = (ULONG)OutputBufferLength; } // Copy our buffer into users. RtlMoveMemory(userChannelData, subQPtr, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength); *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength; } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandlePauseAudio( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_PAUSE_AUDIO Arguments: DeviceExtension - device context Request - request to be handled DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PAGED_CODE (); *DataLength = 0; if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME; cdb.PAUSE_RESUME.Action = CDB_AUDIO_PAUSE; status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10); if (NT_SUCCESS(status)) { PLAY_ACTIVE(DeviceExtension) = FALSE; *DataLength = 0; } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleResumeAudio( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_RESUME_AUDIO Arguments: DeviceExtension - device context Request - request to be handled DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PAGED_CODE (); *DataLength = 0; if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME; cdb.PAUSE_RESUME.Action = CDB_AUDIO_RESUME; status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10); if (NT_SUCCESS(status)) { PLAY_ACTIVE(DeviceExtension) = TRUE; //not in original code. But we should set it. *DataLength = 0; } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleSeekAudioMsf( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_SEEK_AUDIO_MSF Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_SEEK_AUDIO_MSF inputBuffer = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&inputBuffer, NULL); if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; ULONG logicalBlockAddress; logicalBlockAddress = MSF_TO_LBA(inputBuffer->M, inputBuffer->S, inputBuffer->F); ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.SEEK.OperationCode = SCSIOP_SEEK; cdb.SEEK.LogicalBlockAddress[0] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3; cdb.SEEK.LogicalBlockAddress[1] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2; cdb.SEEK.LogicalBlockAddress[2] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1; cdb.SEEK.LogicalBlockAddress[3] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0; status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 10); if (NT_SUCCESS(status)) { *DataLength = 0; } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleStopAudio( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _Out_ size_t * DataLength ) { NTSTATUS status = STATUS_SUCCESS; PAGED_CODE (); *DataLength = 0; if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; cdb.START_STOP.Immediate = 1; cdb.START_STOP.Start = 0; cdb.START_STOP.LoadEject = 0; status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 6); if (NT_SUCCESS(status)) { PLAY_ACTIVE(DeviceExtension) = FALSE; *DataLength = 0; } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleGetSetVolume( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_GET_VOLUME IOCTL_CDROM_SET_VOLUME Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PAGED_CODE (); *DataLength = 0; if (NT_SUCCESS(status)) { ULONG transferSize = MODE_DATA_SIZE; CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb.MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE; cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8); cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF); status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, TRUE, &cdb, 10); if (NT_SUCCESS(status)) { if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME) { PAUDIO_OUTPUT audioOutput; PVOLUME_CONTROL volumeControl; ULONG bytesTransferred; status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, (PVOID*)&volumeControl, NULL); if (NT_SUCCESS(status)) { audioOutput = ModeSenseFindSpecificPage((PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength, CDROM_AUDIO_CONTROL_PAGE, FALSE); // Verify the page is as big as expected. bytesTransferred = (ULONG)((PCHAR)audioOutput - (PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer) + sizeof(AUDIO_OUTPUT); if ((audioOutput != NULL) && (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength >= bytesTransferred)) { ULONG i; for (i=0; i<4; i++) { volumeControl->PortVolume[i] = audioOutput->PortOutput[i].Volume; } // Set bytes transferred in IRP. *DataLength = sizeof(VOLUME_CONTROL); } else { *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; } } } else //IOCTL_CDROM_SET_VOLUME { PAUDIO_OUTPUT audioInput = NULL; PAUDIO_OUTPUT audioOutput = NULL; PVOLUME_CONTROL volumeControl = NULL; ULONG i,bytesTransferred,headerLength; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&volumeControl, NULL); if (NT_SUCCESS(status)) { audioInput = ModeSenseFindSpecificPage((PCHAR)DeviceExtension->ScratchContext.ScratchSrb->DataBuffer, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength, CDROM_AUDIO_CONTROL_PAGE, FALSE); // Check to make sure the mode sense data is valid before we go on if(audioInput == NULL) { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "Mode Sense Page %d not found\n", CDROM_AUDIO_CONTROL_PAGE)); *DataLength = 0; status = STATUS_INVALID_DEVICE_REQUEST; } } if (NT_SUCCESS(status)) { // keep items clean; clear the command history ScratchBuffer_ResetItems(DeviceExtension, TRUE); headerLength = sizeof(MODE_PARAMETER_HEADER10); bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength; // use the scratch buffer as input buffer. // the content of this buffer will not be changed in the following loop. audioOutput = (PAUDIO_OUTPUT)((PCHAR)DeviceExtension->ScratchContext.ScratchBuffer + headerLength); for (i=0; i<4; i++) { audioOutput->PortOutput[i].Volume = volumeControl->PortVolume[i]; audioOutput->PortOutput[i].ChannelSelection = audioInput->PortOutput[i].ChannelSelection; } audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE; audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2; audioOutput->Immediate = MODE_SELECT_IMMEDIATE; RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8); cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF); cdb.MODE_SELECT10.PFBit = 1; status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, bytesTransferred, FALSE, &cdb, 10); } *DataLength = 0; } } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleReadDvdStructure( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DVD_READ_STRUCTURE Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PVOID inputBuffer = NULL; PVOID outputBuffer = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } if (NT_SUCCESS(status)) { status = ReadDvdStructure(DeviceExtension, Request, inputBuffer, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, outputBuffer, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, DataLength); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS ReadDvdStructure( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_opt_ WDFREQUEST OriginalRequest, _In_ PVOID InputBuffer, _In_ size_t InputBufferLength, _In_ PVOID OutputBuffer, _In_ size_t OutputBufferLength, _Out_ size_t * DataLength ) /*++ Routine Description: base function to handle request of IOCTL_DVD_START_SESSION IOCTL_DVD_READ_KEY Arguments: DeviceExtension - device context OriginalRequest - original request to be handled InputBuffer - input buffer InputBufferLength - length of input buffer OutputBuffer - output buffer OutputBufferLength - length of output buffer Return Value: NTSTATUS DataLength - returned data length --*/ { NTSTATUS status = STATUS_SUCCESS; PDVD_READ_STRUCTURE request = (PDVD_READ_STRUCTURE)InputBuffer; PDVD_DESCRIPTOR_HEADER header = (PDVD_DESCRIPTOR_HEADER)OutputBuffer; CDB cdb; USHORT dataLength; ULONG blockNumber; PFOUR_BYTE fourByte; PAGED_CODE (); UNREFERENCED_PARAMETER(InputBufferLength); if (DeviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD) { *DataLength = 0; return STATUS_INVALID_DEVICE_REQUEST; } dataLength = (USHORT)OutputBufferLength; blockNumber = (ULONG)(request->BlockByteOffset.QuadPart >> DeviceExtension->SectorShift); fourByte = (PFOUR_BYTE)&blockNumber; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE; cdb.READ_DVD_STRUCTURE.RMDBlockNumber[0] = fourByte->Byte3; cdb.READ_DVD_STRUCTURE.RMDBlockNumber[1] = fourByte->Byte2; cdb.READ_DVD_STRUCTURE.RMDBlockNumber[2] = fourByte->Byte1; cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3] = fourByte->Byte0; cdb.READ_DVD_STRUCTURE.LayerNumber = request->LayerNumber; cdb.READ_DVD_STRUCTURE.Format = (UCHAR)request->Format; #if DBG { if ((UCHAR)request->Format > DvdMaxDescriptor) { TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n", (UCHAR)request->Format, READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor], dataLength )); } else { TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n", (UCHAR)request->Format, READ_DVD_STRUCTURE_FORMAT_STRINGS[(UCHAR)request->Format], dataLength )); } } #endif // DBG if (request->Format == DvdDiskKeyDescriptor) { cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)request->SessionId; } cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataLength >> 8); cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataLength & 0xff); status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, dataLength, TRUE, &cdb, 12); if (NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdDCCompletion - READ_STRUCTURE: descriptor format of %d\n", request->Format)); RtlMoveMemory(header, DeviceExtension->ScratchContext.ScratchSrb->DataBuffer, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength); // Cook the data. There are a number of fields that really // should be byte-swapped for the caller. TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdDCCompletion - READ_STRUCTURE:\n" "\tHeader at %p\n" "\tDvdDCCompletion - READ_STRUCTURE: data at %p\n" "\tDataBuffer was at %p\n" "\tDataTransferLength was %lx\n", header, header->Data, DeviceExtension->ScratchContext.ScratchSrb->DataBuffer, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength)); // First the fields in the header TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: header->Length %lx -> ", header->Length)); REVERSE_SHORT(&header->Length); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", header->Length)); // Now the fields in the descriptor if(request->Format == DvdPhysicalDescriptor) { ULONG tempLength = (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength > (ULONG)FIELD_OFFSET(DVD_DESCRIPTOR_HEADER, Data)) ? (DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength - FIELD_OFFSET(DVD_DESCRIPTOR_HEADER, Data)) : 0; PDVD_LAYER_DESCRIPTOR layer = (PDVD_LAYER_DESCRIPTOR)&(header->Data[0]); // Make sure the buffer size is good for swapping bytes. if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, StartingDataSector)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: StartingDataSector %lx -> ", layer->StartingDataSector)); REVERSE_LONG(&(layer->StartingDataSector)); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->StartingDataSector)); } if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, EndDataSector)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: EndDataSector %lx -> ", layer->EndDataSector)); REVERSE_LONG(&(layer->EndDataSector)); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->EndDataSector)); } if (tempLength >= RTL_SIZEOF_THROUGH_FIELD(DVD_LAYER_DESCRIPTOR, EndLayerZeroSector)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "READ_STRUCTURE: EndLayerZeroSector %lx -> ", layer->EndLayerZeroSector)); REVERSE_LONG(&(layer->EndLayerZeroSector)); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "%lx\n", layer->EndLayerZeroSector)); } } if (!NT_SUCCESS(status)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "Status is %lx\n", status)); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdDeviceControlCompletion - " "IOCTL_DVD_READ_STRUCTURE: data transfer length of %d\n", DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength)); } *DataLength = DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength; } ScratchBuffer_EndUse(DeviceExtension); return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleDvdEndSession( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DVD_END_SESSION Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDVD_SESSION_ID sessionId = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&sessionId, NULL); if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; DVD_SESSION_ID currentSession = 0; DVD_SESSION_ID limitSession = 0; if(*sessionId == DVD_END_ALL_SESSIONS) { currentSession = 0; limitSession = MAX_COPY_PROTECT_AGID - 1; } else { currentSession = *sessionId; limitSession = *sessionId; } ScratchBuffer_BeginUse(DeviceExtension); do { RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY; cdb.SEND_KEY.AGID = (UCHAR)(currentSession); cdb.SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID; status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12); currentSession++; } while ((currentSession <= limitSession) && NT_SUCCESS(status)); // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleDvdStartSessionReadKey( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DVD_START_SESSION IOCTL_DVD_READ_KEY Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PDVD_COPY_PROTECT_KEY keyParameters = NULL; PVOID outputBuffer = NULL; PAGED_CODE (); if (NT_SUCCESS(status)) { status = WdfRequestRetrieveOutputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, &outputBuffer, NULL); } if (NT_SUCCESS(status) && RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_READ_KEY) { status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&keyParameters, NULL); } if (NT_SUCCESS(status)) { status = DvdStartSessionReadKey(DeviceExtension, RequestParameters.Parameters.DeviceIoControl.IoControlCode, Request, keyParameters, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, outputBuffer, RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, DataLength); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DvdStartSessionReadKey( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ ULONG IoControlCode, _In_opt_ WDFREQUEST OriginalRequest, _In_opt_ PVOID InputBuffer, _In_ size_t InputBufferLength, _In_ PVOID OutputBuffer, _In_ size_t OutputBufferLength, _Out_ size_t * DataLength ) /*++ Routine Description: base function to handle request of IOCTL_DVD_START_SESSION IOCTL_DVD_READ_KEY Arguments: DeviceExtension - device context IoControlCode - IOCTL_DVD_READ_KEY or IOCTL_DVD_START_SESSION OriginalRequest - original request to be handled InputBuffer - input buffer InputBufferLength - length of input buffer OutputBuffer - output buffer OutputBufferLength - length of output buffer Return Value: NTSTATUS DataLength - returned data length --*/ { NTSTATUS status = STATUS_SUCCESS; ULONG keyLength = 0; ULONG result = 0; ULONG allocationLength; PFOUR_BYTE fourByte; PDVD_COPY_PROTECT_KEY keyParameters = (PDVD_COPY_PROTECT_KEY)InputBuffer; PAGED_CODE (); UNREFERENCED_PARAMETER(InputBufferLength); *DataLength = 0; fourByte = (PFOUR_BYTE)&allocationLength; if (IoControlCode == IOCTL_DVD_READ_KEY) { if (keyParameters == NULL) { status = STATUS_INTERNAL_ERROR; } // First of all, initialize the DVD region of the drive, if it has not been set yet if (NT_SUCCESS(status) && (keyParameters->KeyType == DvdGetRpcKey) && DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd) { DevicePickDvdRegion(DeviceExtension->Device); } if (NT_SUCCESS(status) && (keyParameters->KeyType == DvdDiskKey)) { // Special case - need to use READ DVD STRUCTURE command to get the disk key. PDVD_COPY_PROTECT_KEY keyHeader = NULL; PDVD_READ_STRUCTURE readStructureRequest = (PDVD_READ_STRUCTURE)keyParameters; // save the key header so we can restore the interesting parts later keyHeader = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(DVD_COPY_PROTECT_KEY), DVD_TAG_READ_KEY); if(keyHeader == NULL) { // Can't save the context so return an error TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DvdDeviceControl - READ_KEY: unable to allocate context\n")); status = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(status)) { PREAD_DVD_STRUCTURES_HEADER rawKey = OutputBuffer; PDVD_COPY_PROTECT_KEY outputKey = OutputBuffer; // save input parameters RtlCopyMemory(keyHeader, InputBuffer, sizeof(DVD_COPY_PROTECT_KEY)); readStructureRequest->Format = DvdDiskKeyDescriptor; readStructureRequest->BlockByteOffset.QuadPart = 0; readStructureRequest->LayerNumber = 0; readStructureRequest->SessionId = keyHeader->SessionId; status = ReadDvdStructure(DeviceExtension, OriginalRequest, InputBuffer, sizeof(DVD_READ_STRUCTURE), OutputBuffer, sizeof(READ_DVD_STRUCTURES_HEADER) + sizeof(DVD_DISK_KEY_DESCRIPTOR), DataLength); // fill the output buffer, it's not touched in DeviceHandleReadDvdStructure() // for this specific request type: DvdDiskKeyDescriptor if (NT_SUCCESS(status)) { // Shift the data down to its new position. RtlMoveMemory(outputKey->KeyData, rawKey->Data, sizeof(DVD_DISK_KEY_DESCRIPTOR)); RtlCopyMemory(outputKey, keyHeader, sizeof(DVD_COPY_PROTECT_KEY)); outputKey->KeyLength = DVD_DISK_KEY_LENGTH; *DataLength = DVD_DISK_KEY_LENGTH; } else { TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "StartSessionReadKey Failed with status %x, %xI64 (%x) bytes\n", status, (unsigned int)*DataLength, ((rawKey->Length[0] << 16) | rawKey->Length[1]) )); } FREE_POOL(keyHeader); } // special process finished. return from here. return status; } if (NT_SUCCESS(status)) { status = RtlULongSub((ULONG)OutputBufferLength, (ULONG)sizeof(DVD_COPY_PROTECT_KEY), &result); } if (NT_SUCCESS(status)) { status = RtlULongAdd(result, sizeof(CDVD_KEY_HEADER), &keyLength); } if (NT_SUCCESS(status)) { //The data length field of REPORT KEY Command occupies two bytes keyLength = min(keyLength, MAXUSHORT); } } else //IOCTL_DVD_START_SESSION { keyParameters = NULL; keyLength = sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA); status = STATUS_SUCCESS; } if (NT_SUCCESS(status)) { CDB cdb; allocationLength = keyLength; // Defensive coding. Prefix cannot recognize this usage. UNREFERENCED_PARAMETER(allocationLength); ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY; cdb.REPORT_KEY.AllocationLength[0] = fourByte->Byte1; cdb.REPORT_KEY.AllocationLength[1] = fourByte->Byte0; // set the specific parameters.... if(IoControlCode == IOCTL_DVD_READ_KEY) { if(keyParameters->KeyType == DvdTitleKey) { ULONG logicalBlockAddress; logicalBlockAddress = (ULONG)(keyParameters->Parameters.TitleOffset.QuadPart >> DeviceExtension->SectorShift); fourByte = (PFOUR_BYTE)&(logicalBlockAddress); cdb.REPORT_KEY.LogicalBlockAddress[0] = fourByte->Byte3; cdb.REPORT_KEY.LogicalBlockAddress[1] = fourByte->Byte2; cdb.REPORT_KEY.LogicalBlockAddress[2] = fourByte->Byte1; cdb.REPORT_KEY.LogicalBlockAddress[3] = fourByte->Byte0; } cdb.REPORT_KEY.KeyFormat = (UCHAR)keyParameters->KeyType; cdb.REPORT_KEY.AGID = (UCHAR)keyParameters->SessionId; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdStartSessionReadKey => sending irp %p (%s)\n", OriginalRequest, "READ_KEY")); } else { cdb.REPORT_KEY.KeyFormat = DVD_REPORT_AGID; cdb.REPORT_KEY.AGID = 0; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdStartSessionReadKey => sending irp %p (%s)\n", OriginalRequest, "START_SESSION")); } status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, keyLength, TRUE, &cdb, 12); if (NT_SUCCESS(status)) { if(IoControlCode == IOCTL_DVD_READ_KEY) { NTSTATUS tempStatus; PDVD_COPY_PROTECT_KEY copyProtectKey = (PDVD_COPY_PROTECT_KEY)OutputBuffer; PCDVD_KEY_HEADER keyHeader = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer; ULONG dataLength; ULONG transferLength; tempStatus = RtlULongSub(DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength, FIELD_OFFSET(CDVD_KEY_HEADER, Data), &transferLength); dataLength = (keyHeader->DataLength[0] << 8) + keyHeader->DataLength[1]; if (NT_SUCCESS(tempStatus) && (dataLength >= 2)) { // Adjust the data length to ignore the two reserved bytes in the // header. dataLength -= 2; // take the minimum of the transferred length and the // length as specified in the header. if(dataLength < transferLength) { transferLength = dataLength; } TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DvdDeviceControlCompletion: [%p] - READ_KEY with " "transfer length of (%d or %d) bytes\n", OriginalRequest, dataLength, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength - 2)); // Copy the key data into the return buffer if(copyProtectKey->KeyType == DvdTitleKey) { RtlMoveMemory(copyProtectKey->KeyData, keyHeader->Data + 1, transferLength - 1); copyProtectKey->KeyData[transferLength - 1] = 0; // If this is a title key then we need to copy the CGMS flags // as well. copyProtectKey->KeyFlags = *(keyHeader->Data); } else { RtlMoveMemory(copyProtectKey->KeyData, keyHeader->Data, transferLength); } copyProtectKey->KeyLength = sizeof(DVD_COPY_PROTECT_KEY); copyProtectKey->KeyLength += transferLength; *DataLength = copyProtectKey->KeyLength; } else { //There is no valid data from drive. //This may happen when Key Format = 0x3f that does not require data back from drive. status = STATUS_SUCCESS; *DataLength = 0; } } else { PDVD_SESSION_ID sessionId = (PDVD_SESSION_ID)OutputBuffer; PCDVD_KEY_HEADER keyHeader = DeviceExtension->ScratchContext.ScratchSrb->DataBuffer; PCDVD_REPORT_AGID_DATA keyData = (PCDVD_REPORT_AGID_DATA)keyHeader->Data; *sessionId = keyData->AGID; *DataLength = sizeof(DVD_SESSION_ID); } } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleDvdSendKey( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_DVD_SEND_KEY IOCTL_DVD_SEND_KEY2 Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PVOID inputBuffer = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, &inputBuffer, NULL); if (NT_SUCCESS(status)) { status = DvdSendKey(DeviceExtension, Request, inputBuffer, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, DataLength); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DvdSendKey( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_opt_ WDFREQUEST OriginalRequest, _In_ PVOID InputBuffer, _In_ size_t InputBufferLength, _Out_ size_t * DataLength ) /*++ Routine Description: base function to handle request of IOCTL_DVD_SEND_KEY(2) NOTE: cdrom does not process this IOCTL if the input buffer length is bigger than Port transfer length. Arguments: DeviceExtension - device context OriginalRequest - original request to be handled InputBuffer - input buffer InputBufferLength - length of input buffer Return Value: NTSTATUS DataLength - returned data length --*/ { NTSTATUS status = STATUS_SUCCESS; PDVD_COPY_PROTECT_KEY key = (PDVD_COPY_PROTECT_KEY)InputBuffer; ULONG keyLength = 0; ULONG result = 0; PFOUR_BYTE fourByte; PAGED_CODE (); UNREFERENCED_PARAMETER(InputBufferLength); *DataLength = 0; if (NT_SUCCESS(status)) { if ((key->KeyLength < sizeof(DVD_COPY_PROTECT_KEY)) || ((key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)) > DeviceExtension->ScratchContext.ScratchBufferSize)) { NT_ASSERT(FALSE); status = STATUS_INTERNAL_ERROR; } } if (NT_SUCCESS(status)) { status = RtlULongSub(key->KeyLength, sizeof(DVD_COPY_PROTECT_KEY), &result); } if (NT_SUCCESS(status)) { status = RtlULongAdd(result, sizeof(CDVD_KEY_HEADER), &keyLength); } if (NT_SUCCESS(status)) { keyLength = min(keyLength, DeviceExtension->ScratchContext.ScratchBufferSize); if (keyLength < 2) { status = STATUS_INVALID_PARAMETER; } } if (NT_SUCCESS(status)) { PCDVD_KEY_HEADER keyBuffer = NULL; CDB cdb; ScratchBuffer_BeginUse(DeviceExtension); // prepare the input buffer keyBuffer = (PCDVD_KEY_HEADER)DeviceExtension->ScratchContext.ScratchBuffer; // keylength is decremented here by two because the // datalength does not include the header, which is two // bytes. keylength is immediately incremented later // by the same amount. keyLength -= 2; fourByte = (PFOUR_BYTE)&keyLength; keyBuffer->DataLength[0] = fourByte->Byte1; keyBuffer->DataLength[1] = fourByte->Byte0; keyLength += 2; RtlMoveMemory(keyBuffer->Data, key->KeyData, key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.REPORT_KEY.OperationCode = SCSIOP_SEND_KEY; cdb.SEND_KEY.ParameterListLength[0] = fourByte->Byte1; cdb.SEND_KEY.ParameterListLength[1] = fourByte->Byte0; cdb.SEND_KEY.KeyFormat = (UCHAR)key->KeyType; cdb.SEND_KEY.AGID = (UCHAR)key->SessionId; status = ScratchBuffer_ExecuteCdb(DeviceExtension, OriginalRequest, keyLength, FALSE, &cdb, 12); // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleSetReadAhead( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_STORAGE_SET_READ_AHEAD Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PSTORAGE_SET_READ_AHEAD readAhead = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&readAhead, NULL); if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; ULONG blockAddress; PFOUR_BYTE fourByte = (PFOUR_BYTE)&blockAddress; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB cdb.SET_READ_AHEAD.OperationCode = SCSIOP_SET_READ_AHEAD; blockAddress = (ULONG)(readAhead->TriggerAddress.QuadPart >> DeviceExtension->SectorShift); // Defensive coding. Prefix cannot recognize this usage. UNREFERENCED_PARAMETER(blockAddress); cdb.SET_READ_AHEAD.TriggerLBA[0] = fourByte->Byte3; cdb.SET_READ_AHEAD.TriggerLBA[1] = fourByte->Byte2; cdb.SET_READ_AHEAD.TriggerLBA[2] = fourByte->Byte1; cdb.SET_READ_AHEAD.TriggerLBA[3] = fourByte->Byte0; blockAddress = (ULONG)(readAhead->TargetAddress.QuadPart >> DeviceExtension->SectorShift); cdb.SET_READ_AHEAD.ReadAheadLBA[0] = fourByte->Byte3; cdb.SET_READ_AHEAD.ReadAheadLBA[1] = fourByte->Byte2; cdb.SET_READ_AHEAD.ReadAheadLBA[2] = fourByte->Byte1; cdb.SET_READ_AHEAD.ReadAheadLBA[3] = fourByte->Byte0; status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12); if (NT_SUCCESS(status)) { *DataLength = 0; } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; } _IRQL_requires_max_(APC_LEVEL) NTSTATUS DeviceHandleSetSpeed( _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDF_REQUEST_PARAMETERS RequestParameters, _Out_ size_t * DataLength ) /*++ Routine Description: Handle request of IOCTL_CDROM_SET_SPEED Arguments: DeviceExtension - device context Request - request to be handled RequestParameters - request parameter DataLength - transferred data length Return Value: NTSTATUS --*/ { NTSTATUS status = STATUS_SUCCESS; PCDROM_DATA cdData = &(DeviceExtension->DeviceAdditionalData); PCDROM_SET_SPEED inputBuffer = NULL; PAGED_CODE (); *DataLength = 0; status = WdfRequestRetrieveInputBuffer(Request, RequestParameters.Parameters.DeviceIoControl.InputBufferLength, (PVOID*)&inputBuffer, NULL); if (NT_SUCCESS(status)) { ULONG transferSize = 0; CDB cdb; CDROM_SPEED_REQUEST requestType = inputBuffer->RequestType; ScratchBuffer_BeginUse(DeviceExtension); RtlZeroMemory(&cdb, sizeof(CDB)); // Set up the CDB if (requestType == CdromSetSpeed) { PCDROM_SET_SPEED speed = inputBuffer; cdb.SET_CD_SPEED.OperationCode = SCSIOP_SET_CD_SPEED; cdb.SET_CD_SPEED.RotationControl = speed->RotationControl; REVERSE_BYTES_SHORT(&cdb.SET_CD_SPEED.ReadSpeed, &speed->ReadSpeed); REVERSE_BYTES_SHORT(&cdb.SET_CD_SPEED.WriteSpeed, &speed->WriteSpeed); } else { PCDROM_SET_STREAMING stream = (PCDROM_SET_STREAMING)inputBuffer; PPERFORMANCE_DESCRIPTOR perfDescriptor; transferSize = sizeof(PERFORMANCE_DESCRIPTOR); perfDescriptor = DeviceExtension->ScratchContext.ScratchBuffer; RtlZeroMemory(perfDescriptor, transferSize); perfDescriptor->RandomAccess = stream->RandomAccess; perfDescriptor->Exact = stream->SetExact; perfDescriptor->RestoreDefaults = stream->RestoreDefaults; perfDescriptor->WriteRotationControl = stream->RotationControl; REVERSE_BYTES(&perfDescriptor->StartLba, &stream->StartLba); REVERSE_BYTES(&perfDescriptor->EndLba, &stream->EndLba); REVERSE_BYTES(&perfDescriptor->ReadSize, &stream->ReadSize); REVERSE_BYTES(&perfDescriptor->ReadTime, &stream->ReadTime); REVERSE_BYTES(&perfDescriptor->WriteSize, &stream->WriteSize); REVERSE_BYTES(&perfDescriptor->WriteTime, &stream->WriteTime); cdb.SET_STREAMING.OperationCode = SCSIOP_SET_STREAMING; REVERSE_BYTES_SHORT(&cdb.SET_STREAMING.ParameterListLength, &transferSize); // set value in extension by user inputs. cdData->RestoreDefaults = stream->Persistent ? FALSE : TRUE; TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceHandleSetSpeed: Restore default speed on media change set to %s\n", cdData->RestoreDefaults ? "true" : "false")); } status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12); if (NT_SUCCESS(status)) { *DataLength = 0; } // nothing to do after the command finishes. ScratchBuffer_EndUse(DeviceExtension); } return status; }