Assistance with Inverted Call Model

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

> MY_CONTROL_DISK_REQUEST

Do not embed pointers in your IOCTL buffers, that is a complete PIA to get right and almost always done wrong. If all you want is to give the caller a UNICODE_STRING, do not send them back a UNICODE_STRING, just send them back a null terminated buffer (a PWSTR).

// Insert the control IRP into the request queue
IoMarkIrpPending(Irp);
InsertTailList(&controlDeviceExtension->RequestQueue, &Irp->Tail.Overlay.ListEntry);
status = STATUS_PENDING;

you are not marking the IRP as cancelable. Do not roll your own IRP queue, use an IoCSQ instead

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Monday, April 05, 2010 1:29 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] Assistance with Inverted Call Model

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


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Prior to Doron’s comments, I had experimented a little bit more with this concept. I got rid of the PUNICODE_STRING and just went with a straight UNICODE_STRING. I’m not entirely sure this is the best or most effective method that I am using, but at least for now, it appears that all of the MY_DISK_REQUESTS are at least being added correctly. I have not tried sending an IOCTL yet. My code snippets as they are currently are at the bottom of this post.

Do not embed pointers in your IOCTL buffers, that is a complete PIA to get right
and almost always done wrong.
Doron, agreed… I was getting soo confused with all of the memory references and making sure it was being passed back and forth correctly.

If all you want is to give the caller a
UNICODE_STRING, do not send them back a UNICODE_STRING, just send them back a
null terminated buffer (a PWSTR).
Doron, I’m not sure I understand the benefits of using the null terminated PWSTR over using the UNICODE_STRING

you are not marking the IRP as cancelable. Do not roll your own IRP queue, use
an IoCSQ instead
Doron, How would I make the IRP cancelable? Again, I am totally new to this inverted call model so I am trying to learn quickly. Is tracking the IOCTLs not filled in the RequestQueue considered “rolling my own IRP queue”? How would I use the IoCSQ?

typedef struct _MY_CONTROL_DISK_REQUEST {
// The data buffer allows the application to receive arbitrary data
// Note that this is done OUT OF BOUNDS from the IOCTL. Thus, the driver
// is responsible for managing this.
UNICODE_STRING RequestBuffer;//PVOID RequestBuffer;
ULONG RequestBufferLength;// This specifies the size of the response buffer
} MY_CONTROL_DISK_REQUEST, *PMY_CONTROL_DISK_REQUEST;

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;

// 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, MY_DISK_REQUEST, ListEntry);

controlRequest->RequestBuffer = diskRequest->DriveString;
controlRequest->RequestBufferLength = sizeof(diskRequest->DriveString);
status = STATUS_SUCCESS;
}// end if
if(status == STATUS_SUCCESS) {
Irp->IoStatus.Information = sizeof(MY_CONTROL_DISK_REQUEST);
Irp->IoStatus.Status = STATUS_SUCCESS;
}
return status;
} // end MyProcessDiskRequest

from MyDeviceControl:
switch(irpSp->Parameters.DeviceIoControl.IoControlCode){
case IOCTL_MY_GET_DISK:{
// Validate output buffer for disk value
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MY_CONTROL_DISK_REQUEST)) {
Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
Irp->IoStatus.Information = sizeof(MY_CONTROL_DISK_REQUEST);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_BUFFER_OVERFLOW;
}// end if
// Process the request for a disk
status = DARKProcessDiskRequest(Irp);
if (STATUS_PENDING != status) {
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
// Regardless, at this point we return the status back to the caller.
return status;
break;
}

here is where I add the items to the list and try to handle the case where there might be a queued IOCTL.

// First, lock the queues before doing anything else
ExAcquireFastMutex(&controlDeviceExtension->RequestQueueLock);
if (IsListEmpty(&controlDeviceExtension->RequestQueue)) {
// No waiting IOCTLs. We need to insert this into the DriveToScanQueue
// Create a diskRequest for this item
diskRequest = (PMY_DISK_REQUEST) ExAllocatePoolWithTag(PagedPool, sizeof(MY_DISK_REQUEST), ‘rdCO’);

if (!diskRequest) {
// Complete the request, indicating that the operation failed
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
RtlZeroMemory(diskRequest, sizeof(MY_DISK_REQUEST));
// store the values for the diskRequest
diskRequest->DriveString = *pDriveString;
// Lock the second queue
ExAcquireFastMutex(&controlDeviceExtension->DriveToScanQueueLock);
// Insert the diskRequest into the queue
InsertTailList(&controlDeviceExtension->DriveToScanQueue, &diskRequest->ListEntry);
ExReleaseFastMutex(&controlDeviceExtension->DriveToScanQueueLock);
ExReleaseFastMutex(&controlDeviceExtension->RequestQueueLock);
}// end if (IsListEmpty(
else {
// An IOCTL is waiting right now. Remove it from the Request Queue
listEntry = RemoveHeadList(&controlDeviceExtension->RequestQueue);
controlIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
// Now build the request packet here.
controlRequest = (PMY_CONTROL_DISK_REQUEST) controlIrp->AssociatedIrp.SystemBuffer;
controlRequest->RequestBuffer = *pDriveString;
controlRequest->RequestBufferLength = sizeof(*pDriveString);
controlIrp->IoStatus.Status = STATUS_SUCCESS;
controlIrp->IoStatus.Information = sizeof(MY_CONTROL_DISK_REQUEST);
IoCompleteRequest(controlIrp, IO_NO_INCREMENT);
// Release the lock on the Request queue
ExReleaseFastMutex(&controlDeviceExtension->RequestQueueLock);
}// end else

There are no benefits to using PWSTR v. UNICODE_STRING. If the routine
being called requires one or the other, you cannot use a different one.
UNICODE_STRING is a specific structure while ‘Unicode string’ is different
and to some degree a subset of the first. The primary difference is two
counts in the first case and the EOS (end of string) terminator for the
second.

I believe there is a sample that uses cancel safe queues. Read the sample
and the documentation. You can’t write drivers unless you understand these
concepts, but you can write BSOD generators. OSR has some excellent classes
in driver writing where they will show you the fundamentals and answer your
questions.

wrote in message news:xxxxx@ntdev…
> Prior to Doron’s comments, I had experimented a little bit more with this
> concept. I got rid of the PUNICODE_STRING and just went with a straight
> UNICODE_STRING. I’m not entirely sure this is the best or most effective
> method that I am using, but at least for now, it appears that all of the
> MY_DISK_REQUESTS are at least being added correctly. I have not tried
> sending an IOCTL yet. My code snippets as they are currently are at the
> bottom of this post.
>
>>Do not embed pointers in your IOCTL buffers, that is a complete PIA to get
>>right
>>and almost always done wrong.
> Doron, agreed… I was getting soo confused with all of the memory
> references and making sure it was being passed back and forth correctly.
>
>>If all you want is to give the caller a
>>UNICODE_STRING, do not send them back a UNICODE_STRING, just send them
>>back a
>>null terminated buffer (a PWSTR).
> Doron, I’m not sure I understand the benefits of using the null terminated
> PWSTR over using the UNICODE_STRING
>
>>you are not marking the IRP as cancelable. Do not roll your own IRP
>>queue, use
>>an IoCSQ instead
> Doron, How would I make the IRP cancelable? Again, I am totally new to
> this inverted call model so I am trying to learn quickly. Is tracking the
> IOCTLs not filled in the RequestQueue considered “rolling my own IRP
> queue”? How would I use the IoCSQ?
>
>
> typedef struct _MY_CONTROL_DISK_REQUEST {
> // The data buffer allows the application to receive arbitrary data
> // Note that this is done OUT OF BOUNDS from the IOCTL. Thus, the driver
> // is responsible for managing this.
> UNICODE_STRING RequestBuffer;//PVOID RequestBuffer;
> ULONG RequestBufferLength;// This specifies the size of the response
> buffer
> } MY_CONTROL_DISK_REQUEST, *PMY_CONTROL_DISK_REQUEST;
>
> 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;
>
> // 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, MY_DISK_REQUEST, ListEntry);
>
> controlRequest->RequestBuffer = diskRequest->DriveString;
> controlRequest->RequestBufferLength = sizeof(diskRequest->DriveString);
> status = STATUS_SUCCESS;
> }// end if
> if(status == STATUS_SUCCESS) {
> Irp->IoStatus.Information = sizeof(MY_CONTROL_DISK_REQUEST);
> Irp->IoStatus.Status = STATUS_SUCCESS;
> }
> return status;
> } // end MyProcessDiskRequest
>
> from MyDeviceControl:
> switch(irpSp->Parameters.DeviceIoControl.IoControlCode){
> case IOCTL_MY_GET_DISK:{
> // Validate output buffer for disk value
> if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
> sizeof(MY_CONTROL_DISK_REQUEST)) {
> Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
> Irp->IoStatus.Information = sizeof(MY_CONTROL_DISK_REQUEST);
> IoCompleteRequest(Irp, IO_NO_INCREMENT);
> return STATUS_BUFFER_OVERFLOW;
> }// end if
> // Process the request for a disk
> status = DARKProcessDiskRequest(Irp);
> if (STATUS_PENDING != status) {
> IoCompleteRequest(Irp, IO_NO_INCREMENT);
> }
> // Regardless, at this point we return the status back to the caller.
> return status;
> break;
> }
>
> here is where I add the items to the list and try to handle the case where
> there might be a queued IOCTL.
>
> // First, lock the queues before doing anything else
> ExAcquireFastMutex(&controlDeviceExtension->RequestQueueLock);
> if (IsListEmpty(&controlDeviceExtension->RequestQueue)) {
> // No waiting IOCTLs. We need to insert this into the DriveToScanQueue
> // Create a diskRequest for this item
> diskRequest = (PMY_DISK_REQUEST) ExAllocatePoolWithTag(PagedPool,
> sizeof(MY_DISK_REQUEST), ‘rdCO’);
>
> if (!diskRequest) {
> // Complete the request, indicating that the operation failed
> status = STATUS_INSUFFICIENT_RESOURCES;
> break;
> }
> RtlZeroMemory(diskRequest, sizeof(MY_DISK_REQUEST));
> // store the values for the diskRequest
> diskRequest->DriveString = *pDriveString;
> // Lock the second queue
> ExAcquireFastMutex(&controlDeviceExtension->DriveToScanQueueLock);
> // Insert the diskRequest into the queue
> InsertTailList(&controlDeviceExtension->DriveToScanQueue,
> &diskRequest->ListEntry);
> ExReleaseFastMutex(&controlDeviceExtension->DriveToScanQueueLock);
> ExReleaseFastMutex(&controlDeviceExtension->RequestQueueLock);
> }// end if (IsListEmpty(
> else {
> // An IOCTL is waiting right now. Remove it from the Request Queue
> listEntry = RemoveHeadList(&controlDeviceExtension->RequestQueue);
> controlIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
> // Now build the request packet here.
> controlRequest = (PMY_CONTROL_DISK_REQUEST)
> controlIrp->AssociatedIrp.SystemBuffer;
> controlRequest->RequestBuffer = *pDriveString;
> controlRequest->RequestBufferLength = sizeof(*pDriveString);
> controlIrp->IoStatus.Status = STATUS_SUCCESS;
> controlIrp->IoStatus.Information = sizeof(MY_CONTROL_DISK_REQUEST);
> IoCompleteRequest(controlIrp, IO_NO_INCREMENT);
> // Release the lock on the Request queue
> ExReleaseFastMutex(&controlDeviceExtension->RequestQueueLock);
> }// end else
>

I think what Doron was getting at with PWSTR v. UNICODE_STRING was that if the op uses a PWSTR, he won’t have to deal with the embedded ‘Buffer’ pointer that comes with passing a UNICODE_STRING.

mm