Click here to Skip to main content
Click here to Skip to main content

Driver Development Part 4: Introduction to device stacks

By , 27 Mar 2005
 

Introduction

This is the fourth edition of the Writing Device Drivers articles. This article will introduce the idea of device stacks and how devices interact with each other. We will use the previously created example device driver to demonstrate this topic. To do this we will introduce the idea of a “filter” driver in which we will create to attach to our own driver’s device stack.

What is a Device Stack?

A stack is general terminology that can be envisioned as a pile of objects that just sit on top of each other. There is also an algorithm implementation that defines a stack as a method to store temporary objects in which the last object in is the first object out (also known as LIFO). Both descriptions are related. However, a device stack is not an algorithm nor does it have anything to do with temporary objects. Thus the simple description of a pile of objects that simply sit on top of each other is more related.

Sample image

The best example of a device stack would be in relation to a stack of plates. The plates sit on top of each other just like a stack of devices. The other detail to remember is that we say “device stack” not “driver stack”. In the third tutorial we remember that, a single driver can actually implement multiple devices. This means that a stack of devices could all be implemented in a single physical driver. This article and many others however do refer to “device” and “driver” interchangeably even though they are basically separate but related entities.

Filter Drivers

This is a very commonly used buzz word and I’m sure just about anyone who programs has heard of this. A filter driver is a driver that attaches to the top of a stack of devices in an effort of “filter” processing of requests to a device before they reach the device.

You may assume that all devices in a device stack are filters except for the last one but this is not the case. The devices in a device stack aside from filters generally depend on the architecture of that particular device. For example, you usually have higher level drivers that are near the top of the stack. In the most general case these higher level drivers communicate and interact with user mode requests. The devices in the stack start to break down the request for the next level device until the last device in the chain processes the request. Near the bottom of the device stack lie the lower level drivers like “miniport drivers” which may communicate to actual hardware for example.

The best example could be that of the file system. The higher level drivers maintain the notion of files and file system. They understand where the files are stored on the disk perhaps. The lower level drivers know nothing of files and simply understand requests to read sectors on a disk. They also understand how to queue these requests and optimize disk seeks but they have no knowledge of what is actually on the disk or how to interpret the data.

Every filter device that attaches to a device stack is put at the top. This means that, if another filter device attaches to the device stack after yours then it is now on top of you. You are never guaranteed to be at the top of the stack.

To attach to a device stack we will be using the following API implementation.

RtlInitUnicodeString(&usDeviceToFilter, L"\\Device\\Example");

NtStatus = IoAttachDevice(pDeviceObject, 
                       &usDeviceToFilter,
                       &pExampleFilterDeviceContext->pNextDeviceInChain);

This API will actually open a handle to the device in order to attach and then close the handle. When this API attempts to close the handle our driver will be attached to the device stack so we must ensure that the IRP_MJ_CLEANUP and IRP_MJ_CLOSE can be correctly handled and do not cause a problem since they will be called!

There are a few other APIs one is called IoAttachDeviceToStack. This is actually what IoAttachDevice calls after opening a handle to the device.

IRP Handling

The next thing we need to talk about further is IRP handling. The IRP is created and sent to the first device in the device stack. This device can then process the IRP and complete it or pass it down to the next device in the stack. The general rules of an IRP are that when you receive the IRP you own it. If you then pass it down to the next device you no longer own it and can no longer access it. The last device to process the IRP must complete it.

In this example we will be creating IRPs simply for demonstration purposes. The demonstration will be quite simple and we will be sending IRPs to our own driver. There are some aspects of our implementation here which are omitted in our implementation and things done in a non-standard fashion simply because we control all end points. This is a demonstration and very simple. Owning all end points allows us to be more flexible in what we actually implement since we are in total control and can ensure that nothing goes wrong.

There are a number of simple steps that need to be followed when creating an IRP. Depending on the handling of the IRP these can vary a little however we will be going over a very simple case step by step.

Step One – Create the IRP

This is the obvious first step we need to create an IRP. This is very simple you can simply use a function named IoAllocateIrp. The following is a simple code example using the API.

MyIrp = IoAllocateIrp(pFileObject->DeviceObject->StackSize, FALSE);

There are other APIs and macros which can also create an IRP for you. These are quicker ways to help create the IRP and set the parameters. The one thing to watch out for is to make sure that the function you use to create the IRP is able to be called at the IRQL level you will be using. The other part to check is, who is allowed to free the IRP. If the I/O Manager will manage and free the IRP or if you have to do it yourself.

The following is an example of one that sets parameters for us.

MyIrp = IoBuildAsynchronousFsdRequest(IRP_MJ_INTERNAL_DEVICE_CONTROL, 
                                      pTopOfStackDevice,
                                      NULL, 
                                      0, 
                                      &StartOffset,
                                      &StatusBlock);

Step Two – Set the Parameters

This step depends on what functionality you want to do. You would need to setup the FILE_OBJECT, and the IO_STACK_PARAMETER and everything else. In our example we cheat. We don’t provide a FILE_OBJECT and we set minimal parameters. Why? Well, this is just a simple example and we own all end points. Since we are in control of all end points we can essentially do whatever we want with the parameters. However, if you read up on IRP_MJ_xxx and the specific functionality for that driver, such as IOCTL, you will know what you need to set when sending IRPs around. We actually should comply with these mandates as well so other drivers could talk to us but I attempted to just keep this example very simple.

The following code is how we set our IRP parameters.

                 
PIO_STACK_LOCATION pMyIoStackLocation = IoGetNextIrpStackLocation(MyIrp);

pMyIoStackLocation->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

pMyIoStackLocation->Parameters.DeviceIoControl.IoControlCode =
                                 IOCTL_CREATE_NEW_RESOURCE_CONTEXT;


/*
 * METHOD_BUFFERED
 *
 *    Input Buffer = Irp->AssociatedIrp.SystemBuffer
 *    Ouput Buffer = Irp->AssociatedIrp.SystemBuffer
 *
 *    Input Size   =  Parameters.DeviceIoControl.InputBufferLength
 *    Output Size  =  Parameters.DeviceIoControl.OutputBufferLength
 *
 *    Since we are now doing the same job as the I/O Manager,
 *    to follow the rules our IOCTL specified METHOD_BUFFERED
 */  
 pMyIoStackLocation->Parameters.DeviceIoControl.InputBufferLength  = 
                                                      sizeof(FILE_OBJECT);
 pMyIoStackLocation->Parameters.DeviceIoControl.OutputBufferLength = 0;
                 
/*
 * This is not really how you use IOCTL's but  
 * this is simply an example using
 * an existing implementation.  
 * We will simply set our File Object as the SystemBuffer.
 * Then the IOCTL handler will 
 * know it's a pFileObject and implement the code that we
 * had here previously.
 */
                 
 MyIrp->AssociatedIrp.SystemBuffer = pFileObject;
 MyIrp->MdlAddress                 = NULL;

As you notice, we set the “SystemBuffer” to point to our File Object. This is not exactly how we really should have done this. We should have allocated a buffer and copied the data there. That way we could safely have the I/O Manager free the buffer or we could have freed the buffer when we destroy the IRP. Instead though, we did this quick example and we simply don’t allow the I/O Manager to free the IRP and we don’t free the SystemBuffer obviously.

Step Three – Send the IRP down

You need to send the IRP down to the driver. To do this, you simply specify the DEVICE_OBJECT and the IRP in the IoCallDriver API. You can essentially use whatever DEVICE_OBJECT you have. However, if you want to start at the top of the device stack, it’s best to find the top level device object using API’s such as IoGetRelatedDeviceObject. In our example, we have one that does the call to get the top level device and one that simply uses the Device Object we already have. If you read the debug output, you will notice that in the one we don’t go through the filter driver. This is because IoCallDriver is very simple. It just takes the Device Object and finds the appropriate function to call.

NtStatus = IoCallDriver(pFileObject->DeviceObject, MyIrp);

Step Four – Process & Clean up the IRP

The one thing we did before we sent the IRP down was to create a “Completion Routine”. This is a routine that will get notified when the IRP has been completed. We can do a few things in this case, we can allow the IRP to continue so we can do processing on its parameters or we can destroy it. We can also let the I/O Manager free it. This is actually dependent on how you created the IRP. To answer the question of "who should free it", you should read the DDK documentation on the API you used to allocate it. Implementing the wrong method can lead to disaster!

This is a simple example and we simply free it ourselves.

IoSetCompletionRoutine(MyIrp, Example_SampleCompletionRoutine, 
                                           NULL, TRUE, TRUE, TRUE);

...

NTSTATUS Example_SampleCompletionRoutine(PDEVICE_OBJECT DeviceObject, 
                               PIRP Irp, PVOID  Context)
{

    DbgPrint("Example_SampleCompletionRoutine \n");

    IoFreeIrp(Irp);
    return STATUS_MORE_PROCESSING_REQUIRED;
}

You may notice that, sometimes you see code that checks the “STATUS_PENDING” and may wait on an event. In our case we own all end points and this will not happen in this simple example. This is why some of these details are simply being omitted for simplicity. In the next articles, we will expand on these ideas and fill in the missing pieces. It’s important to just digest one piece at a time.

Handling IRPs in your driver

Once you get an IRP, you own that IRP. You can do whatever you want with it. If you process it you must then either complete it when you are done or pass it down to another driver. If you pass it down to another driver you must forget about it. The driver you passed it to is now responsible for completing it.

The example filter driver we have implemented though is a bit different. It wants to process the parameters after we have provided the example driver with the IRP. To do this we must catch the completion and stop it from being completed. This is because we know the lower level driver should and will complete it. So, by setting our own completion routine, we can stop this. This is done with the following code.

    
pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);

IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, 
      PIO_COMPLETION_ROUTINE) ExampleFilter_CompletionRoutine, NULL, 
      TRUE, TRUE, TRUE);
/*
 * IoCallDriver() simply calls the 
 * appropriate entry point in the driver object associated
 * with the device object.  This is 
 * how drivers are basically "chained" together, they must know
 * that there are lower driver so they 
 * can perform the appropriate action and send down the IRP.
 *
 * They do not have to send the IRP down 
 * they could simply process it completely themselves if they wish.
 */

NtStatus = IoCallDriver(
          pExampleFilterDeviceContext->pNextDeviceInChain, Irp);

/*
 * Please note that our 
 * implementation here is a simple one.  We do not take into account 
 * PENDING IRP's oranything complicated.  We assume that once we get  
 * to this locaiton the IRP has alreadybeen completed and our completetion  
 * routine was called or it wasn't completed and we are still able 
 * to complete it here.
 * Our completetion routine makes sure that the IRP is still valid here.
 *
 */

 if(NT_SUCCESS(NtStatus)
 {      /*
         * Data was read?
         */
    if(Irp->IoStatus.Information)
    {
      /*
       * Our filter device is dependent upon the compliation settings of 
       * how we compiled example.sys
       * That means we need to dynamically figure out if we're 
       * using Direct, Buffered or Neither.
       */
      if(DeviceObject->Flags & DO_BUFFERED_IO)
      {

         DbgPrint("ExampleFilter_Read - Use Buffered I/O \r\n");
         /*
          * Implementation for Buffered I/O
          */

         pReadDataBuffer = (PCHAR)Irp->AssociatedIrp.SystemBuffer;
                
         if(pReadDataBuffer 
             && pIoStackIrp->Parameters.Read.Length > 0)
         {                             
             ExampleFilter_FixNullString(pReadDataBuffer, 
                           (UINT)Irp->IoStatus.Information);
         }
       }
       else
       {
          if(DeviceObject->Flags 
               & DO_DIRECT_IO)
          {
              DbgPrint("ExampleFilter_Read - Use Direct I/O \r\n");
               /*
                * Implementation for Direct I/O
                */
               if(pIoStackIrp && Irp->MdlAddress)
               {
                 pReadDataBuffer = MmGetSystemAddressForMdlSafe(
                         Irp->MdlAddress, NormalPagePriority);   
                 if(pReadDataBuffer && 
                     pIoStackIrp->Parameters.Read.Length)
                 {                             
                     ExampleFilter_FixNullString(pReadDataBuffer,
                                     (UINT)Irp->IoStatus.Information);
                 }
                }
           }
           else
           {

              DbgPrint("ExampleFilter_Read - Use Neither I/O \r\n");

              /* Implementation for Neither I/O
               */
              __try {
        
                      if(pIoStackIrp->Parameters.Read.Length > 
                                                0 && Irp->UserBuffer)
                      {
                    
                        ProbeForWrite(Irp->UserBuffer,
                             IoStackIrp->Parameters.Read.Length, 
                             TYPE_ALIGNMENT(char));
                        pReadDataBuffer = Irp->UserBuffer;
                    
                        ExampleFilter_FixNullString(pReadDataBuffer,
                                       (UINT)Irp->IoStatus.Information);
                       }
                    
               } __except( EXCEPTION_EXECUTE_HANDLER ) {
                    
                    NtStatus = GetExceptionCode();     
               }
           }
       }
     
    }
 }
    
/*
 * Complete the IRP
 *
 */

Irp->IoStatus.Status = NtStatus;
IoCompleteRequest(Irp, IO_NO_INCREMENT);

....

NTSTATUS ExampleFilter_CompletionRoutine(
           PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
{
    
 DbgPrint("ExampleFilter_CompletionRoutine Called \r\n");
 /*
  * We need to return
  * "STATUS_MORE_PROCESSING_REQUIRED" so that we can 
  * use the IRP in our driver.If we complete this here we 
  * would not be able to use it and the IRP would be completed.  This
  * also means that our driver
  * must also complete the IRP since it has not been completed yet.
  */
  return STATUS_MORE_PROCESSING_REQUIRED;
}

The IRP will then not be completed because we returned to the I/O Manager that more processing needs to be done. Now we can manipulate the IRP after the IoCallDriver, however we must now complete it when we are done. This is because we stopped the completion of the IRP. Remember our example does not take into account STATUS_PENDING because we own all end points and we are trying to keep this example as simple as possible.

The Filter Example

The example filter driver in this article attaches itself to the driver’s stack that we created in article 3. If you remember that implementation, we were able to communicate between two user mode applications. One problem with doing this is that, if you typed in a number of strings, the user mode application only prints one string while it may have read three. This could have been fixed in the user mode application easily however how much fun would that be?

Instead we have created a filter driver that simply intercepts the IRP after the read and manipulates the IRP return parameters. It removes all the NULL terminators from the string and replaces them with spaces. It then simply NULL terminates the end of the string. It’s not a perfect example obviously, since we overwrite the last character and don’t attempt to even see if we need to, but this is just a simple example.

These examples just do the minimum necessary, so they work and try not to trap (in the simplest case). I would rather provide some explanation with a simple example than a full fledged example with all the bells and whistles. Those can already be found in the DDK and long articles on MSDN which explain everything all at once.

Using the example

To use the example you simply do the same as you did with article 3. The only difference is that, there is now another loader program that you can run after you have already loaded example.sys. This one will load examplefilter.sys and it will attach to example.sys. The user mode programs can run with or without examplefilter.sys. You can run it both ways and see the differences. Entry points all have debug statements so you can follow the code paths.

Conclusion

In this article we learned a little more about IRP handling (for the purpose of understanding device stacks) and device stacks. We also learned how to implement a very simple filter driver. In each article we will attempt to build upon these basic ideas, so that we can further understand how drivers work and how to develop drivers.

The next article in the series will attempt to combine everything learned over these 4 articles and further explain IRP Handling.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Toby Opferman
Engineer Intel
United States United States
Member
Toby Opferman has worked in just about all aspects of Windows development including applications, services and drivers.
 
He has also played a variety of roles professionally on a wide range of projects. This has included pure researching roles, architect roles and developer roles. He also was also solely responsible for debugging traps and blue screens for a number of years.
 
Previously of Citrix Systems he is very experienced in the area of Terminal Services. He currently works on Operating Systems and low level architecture at Intel.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionError 6 of StartService functionmemberWilsonr99022 Aug '12 - 6:23 
Hello, I was testing this example. I've tested the examples from document 1 to 3 and they works fine. But I'm trying to do the same with the sample in this document. It's NOT working well.
 
About the mistake I have...
 
I have included the function GetLastError in order to catch the errors that could appear in the execution and I found it: when trying to start(StartService) the Filter driver after create de service correctly it returns the error 6(6: ERROR_INVALID_HANDLE- Invalid Driver).
 
I cannot find information about how to solve this error or why could it be happening. I'll be waiting for answers. I did search in Windows Driver Kit documentation and there's not such information...
 
Thanks a lot!!!
GeneralMy vote of 5membergndnet12 Jul '12 - 5:22 
great
GeneralAttaching FILE SYSTEM FILTER driver object to underlying target objectmemberChaitanya Joshi22 Oct '08 - 5:13 
Hello.. first of all a grand great Thank You to you for bringing such a valuable series in front of us. I not only enjoyed your articles but have learnt driver development from your articles.
 
Anyways .. my question is regarding "file system filter" drivers. I'm especially stressing on 'file system' word 'coz on msdn I learnt that file system filter drivers are different from other filters that they don't use 'AddDevice'.
 
Now the problem is while attaching filter driver with underlying driver stack. We can still use IoAttachDeviceToDeviceStack() to attach our driver but it takes argument as
IN PDEVICE_OBJECT  TargetDevice
If AddDevice would have been implemented, this parameter can be obtained from
IN PDEVICE_OBJECT PhysicalDeviceObject
parameter that we get in AddDevice(). But this way is closed for file system filter drivers.
 
There is another alternative to call IoGetDeviceObjectPointer() and passing it the desired driver's object name. But how to find desired driver's name? And I want to add my filter to overall filesystem. Thus my qeustion is ...
 
Q. How to obtain TargetDevice without calling IoGetDeviceObjectPointer? And if there is no other alternative, how to obtain desired driver's object name?
 
I'll be grateful to you for the answer. Thanks.
GeneralREAD call from IOCTLmemberRafalPOL3 Oct '07 - 2:01 
Is it possible to send an IRP to the driver from a function inside IOCTL?. To clarify what do I mean:
I have a serial port driver, I am inside function "set wait mask", which is inside the function responsible for DEVICE CONTROL calls, can I call a READ function by creating IRP? How should I do that?
Questionsending an IOCTL to a filter drivermembermerlinos20 Sep '07 - 6:27 
Hi,
 
I'm new to driver development and I'm wondering how I can get a device handle to a filter driver in user space so that I can send an IOCTL to it.
 
My filter driver is at the top of the driver stack.
I've read that the filter driver should call IoRegisterDeviceInterface(...) in its AddDevice routine and that my user space application could then "enumerate the interface GUID and send IOCTLs to the driver." How can I do that? What should I call to "enumerate the interface GUID" and how do I get the symlink to my filter driver (so that I can call CreateFile to get a device handle)?
 
Thanks!

QuestionHow Filter Drivers inteact with IRP'smemberKalium11 Jan '06 - 1:33 
Hi sorry for bothering you again, but last time i asked you a question it helped me from going the wrong way, so now i have to ask again. Smile | :)
 
If i implement a TDI Filter Driver i will be able to get the IRP's destined to for example /Devices/Tcp. What i really do not understand is that whenever i intercept an IRP will i be able to se any data that was sent.
 
Soppose for example that an User Mode application is trying to send something on the network (i.e Internet Explorer) i guess that there will be calls to /Devices/Tcp ( send ) and now that i have an TDI Filter Driver i will get the calls before Tcp does, will i then be able to see the ammount of data that Internet Explorer triyed to send? Is there some way to check in the IRP for User Mode data.
 
From reading an other article on this site my understanding is that a User Mode application can read and write data to drivers through IRP's the driver can simply read/write to some kind of buffer. This should then mean that my filter driver that i want to implement should be able to read data that was sent in a send call, afterall how would it otherwise be possible to send data from an User Mode application.
 
I am new to this thing so sorry if the questions are to simple.
 
P.S
 
Really good articles by the way, After reading this articles i went from knowing nothing to start discussing of how things should work.
 
Thanks again.
 

AnswerRe: How Filter Drivers inteact with IRP'smemberToby Opferman30 Jan '06 - 8:38 
Actually, socket applications use \Device\Afd which I am not sure how compatible that interface is with Sockets. There is an open source project firewall on sourceforge.net which implements a filter driver for both, TDI and \Device\Afd from which you could use their work to derive the actual interface.
 
The IRP contains the data sent to the driver from user mode or from whereever. The IRP will contain this data in one of 3 formats which is listed in my articles (Direct, Buffered or Neither). The I/O Manager will take the data from user mode and put it into one of these formats before passing it to your driver.
 
So you will be able to see everything provided you are the driver on top of the device stack. If another driver comes in after you and puts itself on top of you, you will only see what that driver lets you see after he performs his own filtering.

 
8bc7c0ec02c0e404c0cc0680f7018827ebee
GeneralInteract with external dll'smemberJaVinci22 Nov '05 - 22:40 

Hi!
 
I have a question... Suppose I have a file system filter driver and I want to determine whether a captured access to the file system is allowed or not.
 
Can I invoke a function (to check if that captured access is allowed or not) stored in a dll from inside the driver's code?
 
Thanks a lot!
 
Regards:
 
JaVinci.
 

GeneralRe: Interact with external dll'smemberToby Opferman30 Jan '06 - 8:26 
You can link against other drivers to call functions exported from them however you cannot call or link against a user mode DLL and invoke those functions from within a driver.
 
8bc7c0ec02c0e404c0cc0680f7018827ebee
GeneralRe: Interact with external dll'smemberJaVinci30 Jan '06 - 21:16 

Just what I expected (I'll have to search another solution), thanks a lot for your help!
 


GeneralRe: Interact with external dll'smemberToby Opferman31 Jan '06 - 6:13 
There are undocumented ways to call from kernel to user however there is a solution which you may be able to use. The driver and the user mode application could either share memory or the driver could expose an entry point (IOCTL) for the user mode application to inform the driver of the policies. The driver then just maintains the policy (or shared memory) and accesses this when it needs to.
 
If there is a change the user mode application reissues the IOCTL (or if shared memory just acquires a shared lock and updates the memory).
 
You generally do this through a "control" device which is a secondary device implemented by the driver for such purposes.
 
8bc7c0ec02c0e404c0cc0680f7018827ebee
GeneralRe: Interact with external dll'smemberJaVinci31 Jan '06 - 20:47 

Ok, I'll try it that way, thanks a lot!!!
 

Questionpart 3 somewhere ?membertoxcct7 Nov '05 - 4:06 
hi Toby,
 
i really enloy you drivers articles, but i cannot find out the Part 3 of the serie... i am missing a link ?
 
thanks
 

TOXCCT >>> GEII power
[toxcct][VisualCalc]
AnswerRe: part 3 somewhere ?memberToby Opferman7 Nov '05 - 5:51 
I'm glad you enjoy the articles. They actually put part 3 in a different section!
 
Part 3[^]
 
When in doubt though you can always click on the author's name and go to Articles Submitted[^] to find them.
 
If you like this series I put part of what would have been Part 6 into November C/C++ Journal (http://www.cuj.com/documents/s=8188/cuj0511opferman/0511opferman.html[^])
 
Thanks,
 
Toby
 


 
8bc7c0ec02c0e404c0cc0680f7018827ebee
GeneralI need help with filter driversmemberJaVinci22 Aug '05 - 23:14 
Hi!
 
I'm trying to develop an application that controls and allows (or denies) every event in my computer (process creation and termination, file access, packet filtering, DLL access...) and I want to know if it would be possible to control all that stuff using drivers.
 
I'm using visual c++ 6 under win-xp and I'm going to use my own access control list, which implies that I have to do some file accesses in the process of checking permissions (I check them calling a function inside a dll)
 
My questions are:
- Can I do this using filter drivers? I know I can capture and analize file access requests (including DLL's), and I'm almost sure that I can filter packets, too. But can I use a filter driver to know when an application is trying to be launched or terminated by another application?
- How can I link the functionality I want to develop with the driver? (Once the driver captures an IRP, can I pass the info from the IRP to my application in order to process it?)
- Can it be done in one single application or will I need one for each driver?
 
Thank you so much in advance.
 
JaVinci.
 
PS: I'm not english, so please, forgive my mistakes Smile | :)

GeneralFile Save Filter DrivermemberAmbuj_Lal5 Jun '05 - 20:30 
Hi,
 
I want to force the location to a specific folder when a user tries to save a file. I want to do this by a filter driver in NT, i.e the filter catches all the requests for file save(saveas) and matches with a specific location and denies/gives access if the location given by the user does not match or match, accordingly. How do a write a driver for this?

 
Ambuj
GeneralRe: File Save Filter DrivermemberToby Opferman6 Jun '05 - 9:21 
If you are serious about implementing a File System or File System Filter Driver I would highly suggest that you get the IFS Kit from Microsoft. This can be found at the following URL:
 
IFS Kit[^]
 
There are actually a few methods that could really be used to do what you want. You can implement your own file system driver and reissue IRP's to the original, I believe there's a way to also reject an IRP and have it reissued on a different location, and possibly others (There could even be a simple implementation/framework or method to do this in the IFS kit). I would first suggest that you get familar with the examples in the IFS kit. The following URL contains information on File System Filter drivers:
 
File System Filter Drivers[^]
 

 

 
8bc7c0ec02c0e404c0cc0680f7018827ebee
Generaldriver to driver communicationmembersanket parikh12 May '05 - 4:45 
hi,
It's nice project very helpful for all.I have one problem regarding kernel driver to kernel driver communication.Is this code(ch-4) helpful to me, if, yes, how much modification it required?if, no, then any suggestion regarding same?
Awaiting for reply.
GeneralRe: driver to driver communicationmemberToby Opferman12 May '05 - 18:36 
A few ways to communicate between drivers:
 
1. Function Pointers, have the driver provide a set of function pointers to be called. This was more common in Windows 9x VxD's but is still used in some driver frameworks in NT as well. You just need to use one of the later methods to get the list of function pointers.
 
2. Allocate an IRP and send it to the driver. This example does this and the driver in example 5 also does this.
 
3. You should be able to export functions from one driver and link them into another just as you would a DLL to an EXE. Then just make sure to load the dependent driver second. If there is circular dependencies then look at #1 & #2.
 
Thanks,
 
Toby
 


GeneralRe: driver to driver communicationmemberToby Opferman12 May '05 - 18:40 
There are also "indirect" methods of communications such as memory mapped files (or just an area of memory being used by both drivers they do run in system space so they can see the same memory unless we are talking about display drivers) and events & locks which can be shared among drivers. They can then communicate using locks, shared memory (not really "shared" but common memory) and signal events.
 
There are many ways to do it really you could even be creative and use a user mode process or serivce as a proxy between two drivers.

GeneralFile System Filter DrivermemberFad B23 Apr '05 - 23:04 
Hello
I tried many times to make a file System filter driver, but I could only snif the file calles throw ReadFile & WriteFile, without the ability to snif the Memory maped files
 
Do you have any idea about this problem ?
Thnaks
GeneralRe: File System Filter DrivermemberToby Opferman24 Apr '05 - 9:53 
I know that there are some differences that denote certain reasons why a file is being opened (For memory mapped files, for DLL load, etc.) but I don't remember what they all are. I would do some research on the file system and how different files are opened. You can also get the kernel debugger and set some break points to check how this is done. I assume you are only talking about filtering paging with read/write since obviously the files are "mapped to memory" when they reside in memory.
 
I found this archive email on Memory Mapped Files & Filter Drivers:
 
http://66.102.7.104/search?q=cache:a2xuxTHwgPgJ:www.osronline.com/lists_archive/ntfsd/thread2648.html+IRP_MJ_CREATE+memory+mapped+file&hl=en[^]
 
OSR has a nice driver email list you can sign up for and get questions like these answered by the professionals that work explicitly on those types of drivers.
 


GeneralLayering driversmemberMADCOWIE13 Apr '05 - 20:56 
Hi, i wanna ask how to exactly attach drivers together.Lets say we have 2 drivers. So basically, we should just load both drivers and then use CreateFile to call the lower driver? Does the order of loading the drivers affects the outcome. The driver on top will receive the IRPs send by the user-application to the lower driver?
Sorry, i am very much a newbie driver writer. Please advise.
 
Thanks in Advance.Smile | :)
 
there's no such thing as a stupid programmer...
GeneralRe: Layering driversmemberToby Opferman15 Apr '05 - 9:33 
Hey MadCowie,
 
Let me answer these one at a time.
 
1. Does the order of loading the drivers matter?
 
Yes and No. If one driver requires that the other ALREADY be loaded and needs the other driver to be initialized then yes. If one driver can load successfully without the other then the order should not matter. If a request is made that requires the driver to communicate with the other one and the other is not loaded yet the driver should handle this gracefully. If this communications cannot fail then perhaps it is a requirement that there is an order.
 
In general I would reccomend that the lower level driver loads first then the higher level driver (That is exposed to user mode) loads second and is dependent on the lower. This is because the lower driver essentially is operating independently but the other driver requires the lower. Also, since user mode applications can communicate with it it's exposed and it can't do anything obviously (or can it?) if the lower driver is not there.
 
This is a judgement call you will have to make based on your implementation. Also, do you expect these drivers to be used during boot or system start up? If not then it may not matter however you may want to implement a dependency just for clarity and structure. Say the lower driver fails to load, do you still want to load your higher driver?
 

2. How should I communicate with my other driver?
 
This is dependent on you and what framework you want to implement. Are you implementing towards a specific MS driver frame work? If so you should follow suit. If these are just your drivers and you are completely free to do what you want then doing a ZwCreateFile implementation is fine. Also note that you do not need to use ZwCreateFile, you can actually use IoAllocateIrp() and manually create the IRP_MJ_CREATE. This may be optimal if you are running at certain IRQL levels that ZwCreateFile is not vaild.
 
Have you checked out the driver tutorial Part 5? There is a TDI Client Driver which is doing this model. Although I don't own the TCP driver, you can see how the TDI Client driver communicates to user mode and communicates from there to the TDI Client driver. Although there isn't a "physical" stack connection like the example in this tutorial however you could consider it a "logical" or "virtual" connection between the two drivers.
 
Thanks,
 
Toby

GeneralRe: Layering driversmemberMADCOWIE17 Apr '05 - 16:49 
hmm....i think i will just load the lower driver before loading the upper one to play safe. Ok, i got some further questions. Is it possible if i just have the user-application send IRPs to the upper-driver then the upper driver will allocate custom IRPs to the lower one? How do u create and queue custom IRPs? Does it matter whether i use IRP_MJ_INTERNAL_DEVICE_CONTROL or IRP_MJ_DEVICE_CONTROL to call the IOCTL routines in the lower driver? I hope my questions dun confuse anyone.. Xp
 
Yea, Toby, sorry i haven't checked out ur driver development part 5. That is criminal of me not to. I am going to check it out now!Toby, i can't thank u enuff. The least i could do is to read ur articles and hopefully ask less questions..hehe.
 
there's no such thing as a stupid programmer...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 28 Mar 2005
Article Copyright 2005 by Toby Opferman
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid