Ok, first off let me say I’m trying to follow the example provided at: http://www.osronline.com/article.cfm?id=94
Scenario:
I have a single IOCTL coming from a service that needs to know a UNICODE_STRING representing a symbolic disk name (ie, C:, E:, etc.) determined in the kernel driver. The kernel driver starts at boot time, and begins storing these UNICODE_STRINGs in a structure in that is controlled by a LIST_ENTRY. Once the service begins, it sends the IOCTL asking for a string. If there is no string I want to queue the IOCTL request and complete it once I have a string. Simple enough.
My dilemma is that the OSR example is bouncing stuff back and forth between multiple IOCTLs and queues that I am thoroughly getting LOST! I am seeking assistance for my simple scenario.
So far I have these three structures:
typedef struct _CONTROL_DEVICE_EXTENSION
{
ULONG MagicNumber; // Data structure magic #
UNICODE_STRING RegistryPath; // Registry Path
UNICODE_STRING DriverName; // Driver Name
UNICODE_STRING SymbolicLinkName; // Symbolic Link Name
LIST_ENTRY DriveToScanQueue; // List of drive letters needed to be scanned.
FAST_MUTEX DriveToScanQueueLock; // Drive To Scan Queue Lock
LIST_ENTRY RequestQueue; // Control Request Queue - awaiting dispatch to control threads
FAST_MUTEX RequestQueueLock; // Control Request Queue Lock
} CONTROL_DEVICE_EXTENSION, *PCONTROL_DEVICE_EXTENSION;
typedef struct _MY_DISK_REQUEST {
LIST_ENTRY ListEntry;// This is used to thread the requests onto a DriveToScanQueue
UNICODE_STRING DriveString;
} MY_DISK_REQUEST, *PMY_DISK_REQUEST;
typedef struct _MY_CONTROL_DISK_REQUEST {
PVOID RequestBuffer;
ULONG RequestBufferLength;// This specifies the size of the response buffer
} MY_CONTROL_DISK_REQUEST, *PMY_CONTROL_DISK_REQUEST;
MyDeviceControl() calls NTSTATUS MyProcessDiskRequest(IN PIRP Irp), upon detection of the IOCTL.
The following is my attempt at the inverted call model. I understand to check the driveto scan queue and if there is something there process it, otherwise queue the requested IOCTL until I have something to process it with. Furthemore I understand that what is contained in MY_DISK_REQUEST is in seperate memory from the buffer provided by the user service in the IOCTL, however, that ends my understanding of how to implement this.
NTSTATUS MyProcessDiskRequest(IN PIRP Irp){
PCONTROL_DEVICE_EXTENSION controlDeviceExtension = (PCONTROL_DEVICE_EXTENSION) g_MyDevice->DeviceExtension;
PLIST_ENTRY listEntry = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION irpSp;
PMY_CONTROL_DISK_REQUEST controlRequest;
PMY_DISK_REQUEST diskRequest;
PMDL mdl;
PIRP controlIrp;
PVOID dataBuffer, controlBuffer;
ULONG bytesToCopy;
// First, lock the queues before doing anything else
ExAcquireFastMutex(&controlDeviceExtension->RequestQueueLock);
ExAcquireFastMutex(&controlDeviceExtension->DriveToScanQueueLock);
// Check Drive to scan queue
if (!IsListEmpty(&controlDeviceExtension->DriveToScanQueue)) {
listEntry = RemoveHeadList(&controlDeviceExtension->DriveToScanQueue);
status = STATUS_SUCCESS;
}
else {
controlRequest = (PMY_CONTROL_DISK_REQUEST) Irp->AssociatedIrp.SystemBuffer;
irpSp = IoGetCurrentIrpStackLocation(Irp);
if(!controlRequest || irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MY_CONTROL_DISK_REQUEST)) {
status = STATUS_INVALID_PARAMETER;
}
else {
// Insert the control IRP into the request queue
IoMarkIrpPending(Irp);
InsertTailList(&controlDeviceExtension->RequestQueue, &Irp->Tail.Overlay.ListEntry);
status = STATUS_PENDING;
}
}// end else
// Release both locks
ExReleaseFastMutex(&controlDeviceExtension->DriveToScanQueueLock);
ExReleaseFastMutex(&controlDeviceExtension->RequestQueueLock);
// If we found an entry to process, we need to return the information to the caller here.
if (listEntry) {
// This is the Drive to scan we removed from the queue
// Now we build the request packet
controlRequest = (PMY_CONTROL_DISK_REQUEST) Irp->AssociatedIrp.SystemBuffer;
diskRequest = CONTAINING_RECORD(listEntry, DARK_DISK_REQUEST, pDriveString);
//****** THIS IS WHERE THE CONFUSION IS STARTING ******
mdl = IoAllocateMdl(controlRequest->RequestBuffer,
controlRequest->RequestBufferLength,
FALSE, // should not be any other MDLs associated with control IRP
FALSE, // no quota charged
controlIrp); // track the MDL in the control IRP…
__try {
// Probe and lock the pages
MmProbeAndLockProcessPages(mdl,
IoGetRequestorProcess(controlIrp),
UserMode,
IoWriteAccess);
} __except(EXCEPTION_EXECUTE_HANDLER) {
// Access probe failed
status = GetExceptionCode();
// Cleanup what we were doing…
IoFreeMdl(mdl);
// First, lock the queues before doing anything else
ExAcquireFastMutex(&controlDeviceExtension->RequestQueueLock);
ExAcquireFastMutex(&controlDeviceExtension->DriveToScanQueueLock);
InsertTailList(&controlDeviceExtension->DriveToScanQueue, listEntry);
ExReleaseFastMutex(&controlDeviceExtension->DriveToScanQueueLock);
ExReleaseFastMutex(&controlDeviceExtension->RequestQueueLock);
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
status = STATUS_PENDING;
break;
}
dataBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
controlBuffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
bytesToCopy = IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length;
//****** THIS IS WHERE THE CONFUSION IS TOTAL ********
//controlRequest->RequestBuffer = ??;
//controlRequest->RequestBufferLength = ??;
status = STATUS_SUCCESS;
}
if(status == STATUS_SUCCESS) {
Irp->IoStatus.Information = sizeof(MY_CONTROL_DISK_REQUEST);
}
return status;
}
Additionally I understand when I am locking the queue to add a new MY_DISK_REQUEST with a new UNICODE_STRING, that I should look to see if there is an IOCTL pending on the RequestQueue prior to adding it, but how to again properly handle that queued IOCTL to send it on its way is also confusing me.
Any and all assistance is appreciated.
Brian