(Reproduced)
Windows file system filter driver development tutorial
0. Author, Madman
I have a long-term online project manager acting as a "technical implementator" role. I feel that the development of the Windows file system driver can find less information. In order to let technical experience not forgetting and attract interest in communication, I write this tutorial with my work experience.
My understanding is not necessarily correct, and there is a mistake. Welcome to contact me by questions. We are also happy to accept the development of various drivers. The mailbox is MFC_TAN_WEN @ 163.com, QQ is 16191935.
For this tutorial, you can get free of charge and will be transferred to any website. But do not plagiarize anything as any profit publication all or part.
1. Overview, drilling purposes and preparations
I often encounter online request to develop file system drivers online. Windows's PCs are driven by filtration. Its purpose is no more than the following:
First, it is used for the anti-virus engine. I hope that when the system reads and writes files, capture the data content read and written, and then detects whether the virus code is contained.
The second is to encrypt the file system, and it is desirable to encrypt the data during the file writing process, and decrypt during the read process.
The third is to design a transparent file system acceleration. When reading and writing disks, the appropriate Cache algorithm can greatly improve the working efficiency of the disk. Windows itself's cache algorithm is not suitable for some special reading and writing
Disk operation (such as a streaming media file on the streaming server). Design your own Cache algorithm, I have already feeling at work.
If you just have the above requirements, you can read this tutorial.
The file system driver is one of the most complex driver types in the Windows system. It is impossible to hold too much hope for the help in IFSDDK. In my experience, the file system-related DDK help is extremely simple, and many important parts are only easy to write. If IFSDDK is installed, you should read the documentation under SrcFileSysosR_DOCs. Not just DDK help.
There are few books in file system driver development. Chinese information I just saw a book in Houjie translated, two or three chapters involved, but only only the 9X VXD driver. I have seen a book in the NT file system. I don't remember the book name of these two books.
If you plan to develop a 9X or NT file system driver, I suggest you go online to download the book mentioned above. The two books have free electronic version downloads. If you plan to develop Windows2000WindowsXPWindow2003 file system drivers, you can read this tutorial. Although this tutorial only tells the file system filtering. But if you want to develop a brand new file system driver, this tutorial is still very helpful to you.
Before learning the file system drive development, IFSDDK should be installed on the machine. The more the DDK version is, the more system calls provided in the header file. Some people often ask questions such as XPDDK compilation can not run in 2000. I want to explain this: Advanced version of DDK should always compile low-end-driven code, and the obtained binary version can always run on the low-level system. but
In turn, it may not be possible. If you write a driver on a low-level system on a high-level system, you must pay very serious attention to only the system calls on the low-level system.
IFSDDK can be downloaded for free on some FTP.
My use is IFS DDK for XP, but the two machines I actually develop have a Windows 2000, and the other is Windows 2003. I try to make me compile the driver, can be on the 2000x2003 system. Passed the test.
Installation Configuration DDK and a lot of introductions on the network developed in the VC. After IFSDDK installation, there is an example of file system driver in the FileSys directory under the src directory. Read these code You can quickly learn file system driver development.
Sfilter under the Filter directory is an example of a file system filtering. Another FilesPy is completely more complicated with this example of code.
How to compile this example with DDK asked yourself to view the relevant information.
File system filtering driver After you get a file that extension is SYS. At the same time, you need to write a .inf file to implement this driver installation. I don't discuss the details of the .inf file here, you can modify the INF files under the sfilter directory. Right-click the INF file point Pop-up menu to select "Install", you can install this filter driver. But you must restart the system to take effect.
If the blue screen cannot be started after reboot, you can use other way to boot the system to delete your .sys file in the system32drivers directory. I tried this situation in this case.
It is a blue screen. So I have had to install two 2000 systems on the machine. In the case of dual system, a system crashes to start with another system, and delete the original driver.
If you want to debug code, install Softice.
Open the file Sources in the sfilter directory (this file has no extension), join a line
Browser_info = 1
Then open Symbol Loader, file-> Open Select the xxx.sys, modul-> load, mod-> translate you compile, and then debug it.
Open Softice, enter the file * to see the code.
If you are ready, we can start pondering the development of the Windows file system filtering.
Windows file system filter driver development tutorial
2.Hello World, drive objects and device objects
The drive object mentioned here is a data structure and is named Driver_Object in DDK. Any driver corresponds to a Driver_Object. How do I get Driver_Object written by my drive? The entry function of the driver is DriveRentry, so when you write a driver, you will write down the following code:
NTSTATUS DRIVERENTRY (in PDRIVER_OBJECT DriverObject, In Punicode_String RegistryPath)
{
}
This function is quite similar to the main (). IN in the C language. IN is a meaningless macro, only indicating that the parameters of the back are an input, and the corresponding OUT represents this parameter is a return. There is no reference here, so if you want to return the result in the parameter, you will pass into the pointer.
DriverObject is Driver_Object you wrote to drive, which is assigned when the system is loaded with your driver. RegisteryPath is dedicated to the registry path for you to record your drive-related parameters.
DriverObject is important to have a set of function pointers called Dispatch functions.
The main task of developing drivers is to write these DISPATCH FUNCTIONS. When the system uses your driver, send IRP to your DO (this is a common work mode of all Windows drivers). Your task is to process these requests in the Dispatch function. You can make the IRP fail, or you can return it, or you can modify these IRPs, or even IRP yourself.
The device object refers to DEVICE_OBJECT.
But in fact, each IRP is sent to Do. Only the IRP of the Doo generated by the driver,
The drive will be sent to the drive.
When an application opens a file and reads and writing files, the Windows system be sent to the file system driver.
File system filtering drivers will be able to filter these IRPs. This, you have the ability to capture and change file system operations.
File systems such as FAT32, NTFS, may generate several devices. First, the file system driver itself often generates a control device (CDO). The main task of this device is to modify the internal configuration of the entire driver. So a driver only corresponds to a CDO.
Another device is Volume of this file system mount. A FS may have multiple Volume, or there may be no one. Explain, if you have C:, D:, E:, F: four partitions. C:, D: For NTFS, E:, F: is FAT32. So C:, D: is the two Volume device objects of FAT. In fact, "C:" is the symbolic link name of the device. Not a real device name. You can open Symbolic Links Viewer, you can see:
C: DeviceharddiskVolume1
Therefore, the device of the device is called "DeviceharddiskVolume1".
It is also seen here that the file system driver is for each Volume to generate a DeviceObject instead of each file. In fact, IRP read and written on the file is sent to the Volume device object. There is no "file device object".
If you have these concepts, we now use simple code to generate our CDO, as our first step cow knife trial driven by our development file system.
I don't like to use Microsoft's code. Too long and ugly. I have redefined most of the data structures and functions. To this end, I wrote a header file called WDF.H to help me transform. Interested readers can send mail to request this file. There is no relationship, I always write the original WD_XXX series of things in DDK.
// ----------------- WDF_FILTER.C -------------------------
#include "wdf.h"
WD_STAT WDFF_CDO_CREATE (in wd_drv * driver,
IN wd_size exten_len,
In wd_ustr * name,
OUT WD_DEV ** DEVICE)
{
Return WD_DEV_CREATE
Driver,
EXTEN_LEN,
Name,
WD_DEV_DISK_FS,
WDF_DEV_SECURE_OPEN,
WD_FALSE,
DEVICE);
}
WD_STAT WD_MAIN (in wd_drv * driver,
In wd_ustr * reg_path)
{
WD_USTR NAME;
WD_STAT STATUS = WD_STAT_SUC;
// Then I generate control equipment, although now my control equipment is not dry.
WD_USTR_INIT (& Name, L "/ filesystem / filters / ou_fs_filter);
Status = WDFF_CDO_CREATE (Driver, 0, & Name, & g_cdo);
IF (! WD_SUC (STATUS))
{
IF (status == wd_stat_path_not_found)
{
// This situation occurs in the FileSystemFilters path does not exist. This path is
// Plus it on XP. So 2000 will run here.
WD_USTR_INIT (& Name, L "/ filesystem / ou_fs_filter");
Status = WDFF_CDO_CREATE (Driver, 0, & Name, & g_cdo);
}
IF (! WD_SUC (STATUS))
{
WD_PRINTF0 ("ERROR: CREATE CDO FAILED.RN");
Return status;
}
}
WD_PRINTF0 ("Success: CREATE CDO OK.RN);
Return status;
}
In order to make the code look like the top, I have to do a lot of conversions. Such as
#DEfine Driverentry WD_MAIN
A cool feeling, it is finally working in a function that seems more than the main (). WD_DEV_CREATE This function is called IOCREATEDEVICE. The WD_SUC is actually a macro such as success (). // -------------------------------------------------------------------------------------------------------- ------------
#include "ntifs.h"
#define in in in in
#define out out
#define Optional Optional
#define wd_ustr unicode_string
#define WDP_USTR PUNICODE_STRING
#define WD_MAIN Driverentry
// Equipment, drive object type
TypeDef Driver_Object WD_DRV;
TypedEf device_Object WD_DEV;
Typedef Driver_Object WD_PDRV;
TypeDef PDEvice_Object WD_PDEV;
ENUM {
WD_DEV_DISK_FS = file_device_disk_file_system,
WD_DEV_CDROM_FS = file_device_cd_rom_file_system,
WD_DEV_NETWORK_FS = file_device_network_file_system
}
// State related type and macro
Typedef NTSTATUS WD_STAT;
ENUM {
WD_STAT_SUC = STATUS_SUCCESS,
WD_STAT_PATH_NOT_FOUND = status_Object_path_not_found,
WD_STAT_INSUFFICIENT_RES = status_insuffect_resources,
WD_STAT_INVALID_DEV_REQ = Status_INVALID_DEVICE_REQUEST,
WD_STAT_NO_SUCH_DEV = status_no_such_device,
WD_STAT_IMAGE_ALREADY_LOADED = Status_Image_Already_loaded,
WD_STAT_MORE_PROCESSING = status_more_processing_required,
WD_STAT_PENDING = status_pending
}
_INLINE WD_BOOL WD_SUC (WD_STAT State)
{
Return nt_success (state);
}
#define wd_printf0 dbgprint
_INLINE WD_VOID WD_USTR_INIT (in Out WD_USTR * STR,
In const wd_wchar * chars)
{
RTLinitunicodeString (STR, Chars);
}
_INLINE WD_VOID WD_USTR_INIT_EM (
In out wd_ustr * STR,
In wd_wchar * chars,
In wd_size size)
{
RTLINITEMPTYUNICODESTRING (STR, CHARS, SIZE);
}
WDF.H This file I only exceeded the part you need. You already have a simple "driver" full code. It can even compile, install (please modify the sfilter.inf file, but the method is just change multiple sfilter to "ur_fs_filter", I hope you don't have problems during this process). Then put WDF.H and WDF_FILTER.C in your newly established directory, there should be two files in this directory. One is makefile, copy it from the sfilter directory. The other is Source, please enter the following: TargetName = OUR_FS_FILTER
TargetPath = OBJ
Targettype = driver
Drivertype = fs
Browser_info = 1
Sources = WDF_FILTER.C
After using DDK, you will get our_fs_filter.sys. Take this file with the INF file described in the previous section, press the method described by the following section.
This driver does not work, but you have successfully completed "Hello World".
Windows file system filter driver development tutorial
3. Distribution routine, FAST IO
The previous section only generates a control device object. But don't forget that the main job of driver development is to write a distribution routine (Dispatch functions.). Connect, we already know that your DriverObject is saved in the Driver of the above code. Now I have written a function to specify a default Dispatch Function to it.
//----------------------------------------
TYPEDEF PDRIVER_DISPATCH WD_DISP_FUC;
_inline WD_VOID WD_DRV_SET_DISPATCH (in wd_drv * driver,
IN WD_DISP_FUC DISP)
{
WD_SIZE I;
For (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i )
Driver-> Majorfunction
= Disp;} In the front of WD_MAIN, I only need to add WD_DRV_SET_DISPATCH (Driver, My_DISPATCH_FUNC); specify a default Dispatch Function for this driver. All IRP requests are sent to this function. However, I may not want this function to process too complicated, and I hope to independeado some common requests, such as Read, Write, Create, Close, then I wrote a few functions to set these DISPATCH FUNCTIONS./ /------------------------------------------------------------------------------------------------------------- in wd_drv * driver, in wd_disp_fuc read) {driver-> MajorFunction [IRP_MJ_READ] = read;} _ inline wd_void wd_drv_set_write (in wd_drv * driver, in wd_disp_fuc write) {driver-> MajorFunction [IRP_MJ_WRITE] = write;} wd_void wd_drv_set_create (in wd_drv * driver, in wd_disp_fuc create) {driver-> MajorFunction [IRP_MJ_CREATE] = create; driver-> MajorFunction [IRP_MJ_CREATE_NAMED_PIPE] = create; driver-> MajorFunction [IRP_MJ_CREATE_MAILSLOT] = create;} wd_void wd_drv_set_file_sys_control (in wd_drv * driver, in wd_disp_fuc control) {driver-> MajorFunction [IRP_MJ_FILE_SYSTEM_CONTROL] = control;} wd_void wd_drv_set_clean_up (in wd_drv * driver, in wd_disp_fuc clean_up) {driver-> MajorFunction [IRP_MJ_CLEANUP] = clean_up;} wd_void wd_drv_set_close (in wd_drv * driver, in wd_disp_fuc close) {driver-> majorfunction [IRP_MJ_CLOSE] = close;} Don't look at me N multi-code, actually set up DRIVER -> Majorfunction This array is. Therefore wd_main set of dispatch functions, it becomes a bottom like this: // begin setting up several distribution routines wd_drv_set_dispatch (driver, my_disp_default); wd_drv_set_create (driver, my_disp_create); wd_drv_set_clean_up (driver, my_disp_clean_up); wd_drv_set_file_sys_control (driver , MY_DISP_FILE_SYS_CTL); WD_DRV_SET_CLOSE (DRIVER, MY_DISP_CLOSE); WD_DRV_SET_READ (DRIVER, MY_DISP_READ); WD_DRV_SET_WRITE (DRIVER, MY_DISP_WRITE); The following tasks are written in the MY_XXX series. These functions. But for this DRIVEROBJECT setting, it is not just as simple as it is.
Since your driver will bind to the upper side of the file system driver, the file system has to handle the normal IRP, and the so-called Fastio.fastio is a request raised by the Cache Manager call. In other words, in addition to normal Dispatch Functions, you have to write another group of FAST IO functions for DriverObject. This group of functions are in driver-> fastiodispatch. I don't know if this pointer will leave the blank will cause the system to crash. There is no space here, so in order to save this set of pointers, you must assign space yourself. Here is the memory allocation function I have used. //---------------------------------------- // simple function memory assignment can specify paging nonpaged _inline wd_pvoid wd_malloc (wd_bool paged, wd_size size) {if (paged) return ExAllocatePool (PagedPool, size); elsereturn ExAllocatePool (NonPagedPool, size);} // release the memory _ inline wd_void wd_free (wd_pvoid point) {ExFreePool (point);} _ inline wd_void wd_memzero (wd_pvoid point, wd_size size) {RtlZeroMemory (point, size);} base with the top, I can write an initialization FastIoDispatch function pointers . //-----------------wdf.h code -------------------- WD_BOOL WD_FIO_DISP_INIT ( wd_drv * driver, wd_ulong size) {wd_fio_disp * disp = wd_malloc (wd_false, size); if (disp == wd_null) return wd_false; wd_memzero ((wd_pvoid) disp, size); driver-> FastIoDispatch = disp; driver-> FastIoDispatch -> sizeoffastiodispatch = size; return wd_true;} This function assigns enough space for the FastiodispACTH pointer and fill in its size. Below is a series of functions to set this function pointer array. In fact, the FASTIO interface function is too much, so I just write several examples of these setup functions: //----------------wdf.h code ----------------------_ inline wd_void wd_fio_disp_set_query_standard (wd_drv * driver, wd_fio_query_standard_func func) {driver-> FastIoDispatch-> FastIoQueryStandardInfo = func;} _ inline wd_void wd_fio_disp_set_io_lock (wd_drv * driver, wd_fio_io_lock_func func) {driver-> FastIoDispatch-> FastIoLock = func;} _ inline wd_void wd_fio_disp_set_io_unlock_s (wd_drv * driver, wd_fio_unlock_single_func func) {driver-> FastIoDispatch-> FastIoUnlockSingle = func;} ... well, if you insist on reading Here, I should congratulate.
Let's review, which work should be done in WD_MAIN. a. Generate a control device. Of course you must give the control settings to the control settings. b. Set up dispatch functions.c. Setting FAST IO Functions.// -------------------------------------------------------------------------------------------------------------------------- -------------- WD_DEV * g_cdo = null; wd_stat wd_main (in wd_drv * driver, in wd_ustr * reg_path) {wd_ustr name; wd_stat status = wd_stat_suc; // then I generate control devices Although now my control device does not dry WD_USTR_INIT (& Name, L "/ filesystem / filters / ou_fs_filters"); Status = WDFF_CDO_CREATE (Driver, 0, & Name, & g_cdo); if (! Wd_suc (status)) {IF ( Status == wd_stat_path_not_found) {// This situation occurs in the FileSystemfilters path does not exist. This path is // plus it in XP. So 2000 may run to WD_USTR_INIT (& Name, L "/ filesystem / ou_fs_filters"); Status = WDFF_CDO_CREATE (DRIVER, 0, & Name, & g_cdo);}; if (! WD_SUC (status)) {wd_printf0 ("error: create cdo failed.rn "); return status;}} wd_printf0 (" success: create cdo ok.rn "); // set several dispatch routine begins wd_drv_set_dispatch (driver, my_disp_default); wd_drv_set_create (driver, my_disp_create); wd_drv_set_clean_up (driver, my_disp_clean_up); wd_drv_set_file_sys_control (driver, my_disp_file_sys_ctl); wd_drv_set_close (driver, my_disp_close); wd_drv_set_read (driver, my_disp_read); wd_drv_set_write (driver, my_disp_write);! // specified fast io handler if (wd_fio_disp_init (driver, sizeof ( WD_FIO_DISP)) {wd_dev_del (g_cdo); WD_PRINTF0 ("ERROR: FAST IO DISP INIT FAILED.RN"); Return WD_STAT_INSUFFICIENT_RES;} // The following specified these functions are defined in WDF_FILTER_FIO.H, in fact, these functions / / a return of falsewd_fio_disp_set_check (driver, my_fio_check); wd_fio_disp_set_read (driver, my_fio_read); wd_fio_disp_set_write (driver, my_fio_write); wd_fio_disp_set_query_basic (driver, my_fio_query_basic_info); ...} an unknown number number FastIo function, I just think a lot. So don't plan all columns, to perfuse it in "...". Some readers may think that these codes cannot debug installation. In fact, you can refer to the sample yourself in Sfilter yourself to complete these code. Now our MY_XXX series has not started writing yet, so the driver cannot be compiled. In the next content, it will gradually introduce.
Windows file system filtering driver development tutorial 4. Equipment stack, filter, file system perception
In front of the archive system driver structure, but I haven't said that our filter driver can capture all IRPs that send to the file system, let us handle it yourself? The device object has been explained. Now explain the equipment stack.
Any device object exists in a device stack. The equipment stack is naturally a set of equipment objects. These equipment objects are associated with each other, that is, if you get a DO pointer, you can know the equipment stack it.
Any request from the application is finally translated by Windows IO MGR to IRP, always transmitted to the top of the device stack.
Original IRP IRP IRP
--------------> ------> -------> ----->
DevTop dev2 ... devvolumne ...?????
<--------------- <------------- <-----
Original IRP (Back) IRP IRP IRP
The arrow to the right indicates the transmission process of the IRP request, and it is returned to the left. It can be seen that IRP starts from the top of the equipment stack and is gradually seated downward. Devvolumue means that we actually filter the Volume device, DevTop represents the top of this device stack. As long as we bind a device at the top of this equipment stack, the request sent to Volume will naturally send us the device.
There is a system call to bind our device to the top of the device stack of a device. This call is oattachdeventodeodeventack, this call 2000 and the above system can be used (so saying this is because there is another oattachdeviceetodeventAcksafe, which is not available. This often causes your Filter to not be used under 2000.)
I wrote a function to help me implement the binding function:
//----------------------------------------------------------------------------------------------------------- ----------------
// This routine binds the source device to the device stack of the target device and returns straight back to the source device.
// A bound device. Note that the source device does not necessarily bind directly to the target device. It should be bound to
// Top of the device of the target device.
_INLINE WD_STAT WD_DEV_ATTACH (in wd_dev * src,
IN WD_DEV * DST,
IN out wd_dev ** attached)
{
* attached = DST;
* attached = ioattachdevicetodeventack (src, dst);
IF (* attached == null)
Return WD_STAT_NO_SUCH_DEV;
Return WD_STAT_SUC;
}
Here, we already know how to filter the request for Volume. For example, "C:" This device, I know that the symbol connection is "C:", it is not difficult to get the device name. After getting the device name, it is not difficult to get the device. At this time, we ioCreateDevice () generate a Device Object, then call WD_DEV_ATTACH binding, isn't it OK? All IRPs sent to "C:" must be sent to our driver first, we can also capture all the operations of the file!
This is really a very simple processing method. The code I get the Filemon is handled like this. If you don't want to deal with dynamic volume, you can do this. But we have a higher demand here. When you insert a U disk into the USB port, when Volume, a "J:", we still have to capture this event and generate a device to bind it.
A new storage medium is discovered and generated in the file system. The process of generating a Volume is called mounting. When the process begins, the FS's CDO will get an IRP, and its Major Function Code is IRP_MJ_FILE_SYSTEM_CONTROL, Minor Function Code is IRP_MN_MOUNT. In other words, if we have generated a device binding file system's CDO, then we can get such an IRP, knowing a new Volume is being mount. At this time we can perform the above operations. The current problem is how to know those file systems in the system, and what I should bind their control devices.
IRegisterfsRegistrationChange () is a very useful system call. This call registers a callback function. When any file system is activated or canceled, the callback function you registered will be called.
//----------------------------------------------------------------------------------------------------------- ----------------
WD_STAT WDFF_REG_NOTIFY
IN WD_DRV * DRIVER,
IN WDFF_NOTIFY_FUNC FUNC
)
{
Return IOREGISTERFSREGISTRATIONCHANGE (DRIVER, FUNC);
}
You need to write a callback function for this.
// ------------------- My callback processing function ------------------------ ------------
WD_VOID MY_FS_NOTIFY
IN WD_DEV * DEV,
In wd_bool activive)
{
WD_WCHAR NAME_BUF [WD_DEV_NAME_MAX_LEN];
WD_USTR NAME;
WD_USTR_INIT_EM (& Name, Name_buf, WD_DEV_NAME_MAX_LEN);
// If you are registered, you should get a notification.
WD_PRINTF0 ("Notify: A File Sys Have Been AcitVed !!! RN");
/ / Get the name of the file system object, then print it out
WD_OBJ_GET_NAME (DEV, & NAME);
WD_PRINTF0 ("Notify: File Sys Name =% WZRN", & Name
IF (Active)
{
WD_PRINTF0 ("Notify: try to attach.rn);
// ... please bind the file system control device
}
Else
{
WD_PRINTF0 ("Notify: Unactive.r");
// ...
}
}
How should I bind a file system CDO? We will describe in detail in the following chapter.
Now we should add the following contents in the WD_MAIN function:
IF (WDFF_REG_NOTIFY (DRIVER, MY_FS_NOTIFY)! = WD_STAT_SUC)
{
WD_PRINTF0 ("Error: REG NOTIFY FAILED.RN");
WD_FIO_DISP_RELEASE (DRIVER);
WD_DEV_DEL (G_CDO);
g_cdo = wd_null;
Return WD_STAT_INSUFFICIENT_RES;
}
WD_PRINTF0 ("Success: Reg Notify OK.N);
We review it again, which work should be done in WD_MAIN.
a. Generate a control device. Of course you must give the control settings to the control settings.
b. Set the Dispatch Functions.
c. Set FAST IO Functions.
d. Write a My_FS_NOTIFY callback function, bind the fs cdo.e.E., use WDFF_REG_NOTIFY to call registration this callback function.
Windows file system filter driver development tutorial
5. Bind FS CDO, file system identifier, device extension
In the last section, we intend to bind a FS CDO just activated. Say that simple call wd_dev_attach can be easily bound this device. However, not every time my_fs_notify call, I found new FS activation, I will bind it directly.
First determine if I need to care about the file system type. I use the following functions to get the device type.
// ------------------ WDF.H in -------------------
_INLINE WD_DEV_TYPE WD_DEV_GET_TYPE (in WD_DEV * DEV)
{
Return dev-> DeviceType;
}
The CDO device type of the file system has several possibilities, and your filter driver may only be interested in it.
ENUM {
WD_DEV_DISK_FS = file_device_disk_file_system,
WD_DEV_CDROM_FS = file_device_cd_rom_file_system,
WD_DEV_NETWORK_FS = file_device_network_file_system
}
You should write a function to determine if the FS is concerned.
/ / ------------- a function, judgment if I care about the fs ---------------
WD_BOOL MY_CARE (WD_ULONG TYPE)
{
Return ((Type) == wd_dev_disk_fs) ||
((TYPE) == WD_DEV_CDROM_FS) ||
((Type) == WD_DEV_NETWORK_FS));
}
The next question is that I intend to skip the file system identifier. The file system identifier is a small substitute for the file system driver. In order to avoid the file system driver that does not use the kernel memory, the Windows system does not load these large drivers, instead of driving the corresponding file system identifier with the file system. When a new physical storage medium enters the system, the IO Manager will "identify" it in turn attempt to identify it. Identify success, immediately load the real file system driver, and the corresponding file system identifier is unloaded. For us, the file system identifier's control device looks like a file system control device. But we don't intend to bind it.
The way to distinguish is the name of the drive. All the names of the driving object of the file system recognizer (Note that DriverObject instead of DeviceObject!) Is "FileSystemfs_rec".
// ------------------- Use these code to skip file system recognizer ----------------- ---
WD_WCHAR NAME_BUF [WD_DEV_NAME_MAX_LEN];
WD_USTR NAME, TMP;
WD_USTR_INIT_EM (& Name, Name_buf, WD_DEV_NAME_MAX_LEN);
WD_USTR_INIT (& TMP, L "/ filesystem / fs_rec");
/ / I don't bind the identifier. So if it is an identifier, I will return successfully. Check if it is identified
The method of // is to see if it is a device for FileSystemFS_REC.
WD_OBJ_GET_NAME (WD_DEV_DRV (FS_DEV), & Name);
IF (WD_USTR_CMP (& Name, & TMP, WD_TRUE) == 0)
{
WD_PRINTF0 ("ATTACH FS dev: is a recogonizer.r");
Return WD_STAT_SUC;}
WD_PRINTF0 ("Attach Fs Dev: Not a Recogonizer.r");
Next, I will generate my device. Here is the concept of the device extension. The device object is a data structure. In order to represent different devices, there will be a custom space, which is used to record the unique information of this device. We determine the equipment we generated as follows:
// File filtering system driven device extension
Typedef struct _my_dev_ext
{
// We bind file system driver
WD_DEV * ATTACHED_TO;
// The device name of the device on the upper device.
WD_USTR dev_name;
// This is the buffer of the Unicode string on the upper side
WD_WCHAR NAME_BUF [WD_DEV_NAME_MAX_LEN];
} MY_DEV_EXT;
The reason is so simple, because we have not much something to record now. Just remember which device you bind it is fine. If more information is required in the future, it will not be later. The size of the extended space is specified when WDF_DEV_CREATE (that is, this device is generated). After getting the device object pointer, I use the following function to get the device extension pointer:
// ------------------------------
_INLINE WD_VOID * WD_DEV_EXT (WD_DEV * DEV)
{
Return (dev-> deviceextension);
}
After generating the device, in order to make the system look, your device and the original device have no difference, you must set some of the logo of the device to be the same as the device you bind.
_INLINE WD_VOID WD_DEV_COPY_FLAG (WD_DEV * NEW_DEV,
WD_DEV * OLD_DEV)
{
IF (old_dev-> flags & do_buffered_io)
New_dev-> flags & = do_buffered_io;
IF (Old_DEV-> Flags & do_direct_io)
NEW_DEV-> FLAGS & = DO_DIRECT_IO;
IF (Old_DEV-> Characteristics & File_Device_Secure_Open)
NEW_DEV-> Characteristics & = file_device_secure_open
}
DO_BUFFERED_IO, DO_DIRECT_IO The meaning of these two flags is that the buffer address used will vary when the external device is sent to these devices. This will then discuss when you read and write in the filter file. Now everything is finished, you should go to the Do_Device_Initializing flag on your new device to indicate that the device is fully available.
// ------------------------------
_INLINE WD_VOID WD_DEV_CLR_INIT_FLAG (WD_DEV * DEV)
{
DEV-> Flags & = ~ do_device_initializing;
}
Now I write a function to complete the above process. You only need to call this function in the position prompt in the previous section, complete the binding of the file system control device.
// ----------- Bind a file system drive device -------------------------
WD_STAT MY_ATTACH_FS_DEV (WD_DEV * FS_DEV)
{
WD_WCHAR NAME_BUF [WD_DEV_NAME_MAX_LEN];
WD_USTR NAME, TMP;
WD_DEV * New_DEV;
WD_STAT STATUS; MY_DEV_EXT * EXT;
WD_USTR_INIT_EM (& Name, Name_buf, WD_DEV_NAME_MAX_LEN);
WD_USTR_INIT (& TMP, L "/ filesystem / fs_rec");
// If it is not the type I care, I will return successfully.
IF (! my_care (wd_dev_get_type (fs_dev))))
{
WD_PRINTF0 (("Attach Fs dev: not a cared type.r"));
Return WD_STAT_SUC;
}
WD_PRINTF0 ("ATTACH FS dev: is my card type.r");
/ / I don't bind the identifier. So if it is an identifier, I will return successfully. Check if it is identified
The method of // is to see if it is a device for FileSystemFS_REC.
WD_OBJ_GET_NAME (WD_DEV_DRV (FS_DEV), & Name);
IF (WD_USTR_CMP (& Name, & TMP, WD_TRUE) == 0)
{
WD_PRINTF0 ("ATTACH FS dev: is a recogonizer.r");
Return WD_STAT_SUC;
}
WD_PRINTF0 ("Attach Fs Dev: Not a Recogonizer.r");
/ / Now generate a device to bind
Status = WD_DEV_CREATE (g_drv, sizeof (my_dev_ext), NULL,
WD_DEV_GET_TYPE (fs_dev),
0, WD_FALSE, & New_DEV);
IF (! WD_SUC (STATUS))
{
WD_PRINTF0 ("ATTACH FS dev: dev: dev create failed.r");
Return status;
}
WD_PRINTF0 ("ATTACH FS dev: create dev success.r");
/ / Next, the various flags of the device are set to the flag to be bound.
WD_DEV_COPY_FLAG (New_DEV, FS_DEV);
EXT = (my_dev_ext *) WD_DEV_EXT (New_DEV);
WD_PRINTF0 ("Begin to attach.r");
Status = WD_DEV_ATTACH (new_dev, fs_dev, & ext-> attached_to);
WD_PRINTF0 ("Attach over.status =% 8xRn", Status;
IF (! WD_SUC (STATUS))
{
WD_PRINTF0 ("ATTACH FS dev: dev attach failed.r");
Unreferenced_parameter (new_dev);
WD_DEV_DEL (New_DEV);
Return status;
}
WD_PRINTF0 ("attach fs dev: attach% wz succeed.r", & name);
WD_USTR_INIT_EM (& EXT-> dev_name, ext-> name_buf, wd_dev_name_max_len);
WD_USTR_COPY (& EXT-> dev_name, & name);
WD_DEV_CLR_INIT_FLAG (new_dev);
Return status;
}
Windows file system filter driver development tutorial
6.IRP transmission, File System Control Dispatch
We have to start writing Dispatch Functions because your device has been bound to the file system control device. The request sent to the file system to send you the driver. If you can't do proper processing, your system will crash. The simplest processing method is to pass the request that is not changed to the device we bind. How to get the equipment we bind? The previous section has recorded the device in our device extension.
// ------------ I use this function to get the device I binded -----------
// Get a bound device
_INLINE WD_DEV * MY_DEV_ATTACHED (WD_DEV * DEV)
{
RETURN ((WDFF_DEV_EXT *) WD_DEV_EXT (DEV)) -> attached_to;
}
How to transfer requests? Using IOCALLDRIVER, the first parameter of the call is the device object pointer, the second parameter is an IRP pointer.
An IRP has a group of IO_STACK_LOCATION. As a previous IRP passed in a device stack. IO_STACK_LOCATION is corresponding to this device stack. The partial parameters used to save the IRP request in the current device stack position. If I want to pass the request to the next device, then I should copy the current IO_STATATICK_LOCATION to the next.
I have written some functions to handle IO_STACK_LOCATION, and the other WD_IRP_CALL is used to package the IOCALLDRIVER function.
//--------------------------------------- -------
TypDef wd_irpsp pio_stack_locaion;
_INLINE WD_IRPSP * WD_CUR_IO_STACK (WD_IRP * IRP)
{
Return IOGETCURRENTIRPSTACKLOCATION (IRP);
}
_INLINE WD_VOID WD_SKIP_IO_STACK (WD_PIRP IRP)
{
IoskipCurrentirPstackLocation (IRP);
}
_INLINE WD_VOID WD_COPY_IO_STACK (WD_IRP * IRP)
{
IocopyCurrentirPstackLocationToneXT (IRP);
}
_INLINE WD_STAT WD_IRP_CALL (WD_DEV * DEV, WD_PIRP IRP)
{
Return IocallDriver (dev, IRP);
}
With these, I can now write a default Dispatch Functions.
/ / The default processing is simple, ignore the current call stack, send it directly to the binding device
WD_STAT MY_DISP_DEFAULT (in WD_DEV * DEV, IN WD_PIRP IRP)
{
WD_DEV * ATTACHED_DEV;
IF (! is_my_dev (dev))
Return WD_IRP_FAILED (IRP, WD_STAT_INVALID_DEV_REQ);
IF (IS_MY_CDO (dev))
Return WD_IRP_FAILED (IRP, WD_STAT_INVALID_DEV_REQ);
attached_dev = my_dev_attached (dev);
IF (! attached_dev)
Return WD_IRP_FAILED (IRP, WD_STAT_INVALID_DEV_REQ);
WD_SKIP_IO_STACK (IRP);
Return WD_IRP_CALL (Attached_Dev, IRP);
}
There is a function is_my_dev on the upper side to determine if my device is. This judgment process is simple. You can get the DriverObject pointer through the DEV, and it is determined whether I can drive my own driver. Is_my_cdo () to determine if this device is my control device, don't forget that we first generated a drive-driven control device in WD_MAIN (). The actual control device does not do anything, so any requests it happen is illegal. Return to an error. WD_IRP_FAILD This function immediately made an IRP failed. The content is as follows: // This function can fail to fail out an IRP
_INLINE WD_STAT WD_IRP_FAILED (WD_PIRP IRP, WD_STAT STATUS_ERROR)
{
IRP-> iostatus.status = status_error;
IRP-> iostatus.information = 0;
Return WD_IRP_OVER (IRP);
}
In this way, I don't change my driver IRP, return to an error illegal request. But in fact, this situation is rare.
If you want your driver to run right now, let all DispactH functions call my_disp_default. This driver can be bound to the file system's control device and output some debugging information. But there is no bind Volume. So you can't directly monitor the file read and write.
For a binding file system control device, other requests are directly called directly to the default processing. The focus should be aware that the function MY_DISP_FILE_SYS_CTL () of the DISPATCH processing of IRP_MJ_FILE_SYSTEM_CONTOL has been hung.
IRP_MJ_FILE_SYSTEM_CONTROL This thing is the main function number of IRP. There is generally a secondary function number under each primary function number. These two things indicate an IRP function.
The primary function number and the second function number are the beginning of IO_STACK_LOCATION.
// ---------------- I redefined the function number -------------------
ENUM {
WD_IRP_MN_MOUNT = IRP_MN_MOUNT_VOLUME,
WD_IRP_MN_LOAD_FILESYS = IRP_MN_LOAD_FILE_SYSTEM,
WD_IRP_MN_USER_REQ = IRP_MN_USER_FS_REQUEST
}
ENUM {
WDF_FSCTL_DISMOUNT = fsctl_dismount_volume
}
To get a function number, you must first get the current IO_STACK_LOCATION, which already has a function wd_cur_io_stack on this, I believe this can't be difficult to fall.
When there is Volumne by mount or dismount, you wrote my_disp_file_sys_ctl () is called. For specific judgment methods, you will see the following code:
// You can see other functions in the distribution function is very simple, but file_sys_ctl
// Treatment will be more complicated. We have bind the file system driver in the Notify function.
// Object. When the file system gets the actual medium, a new device object is generated.
// This device is called Volume, and this device is born in Mount in File_SYS
// Cheng, and it is also pinned in unmount. After we capture such an operation, we will
/ / Must generate our device objects, bind it on such a "volume" to bind this volume
// The operation of the file.
WD_STAT MY_DISP_FILE_SYS_CTL (in WD_DEV * DEV, IN WD_PIRP IRP)
{
WD_DEV * ATTACHED_DEV;
WD_IO_STACK * STACK = WD_CUR_IO_STACK (IRP); if (! Is_MY_DEV (DEV))
Return WD_IRP_FAILED (IRP, WD_STAT_INVALID_DEV_REQ);
Switch (WD_IRPSP_MINOR (STACK))
{
Case WD_IRP_MN_MOUNT:
// Here, a Volume is being in mount
RETURN MY_FSCTL_MOUNT (DEV, IRP);
Case WD_IRP_MN_LOAD_FILESYS:
Return my_fsctl_load_fs (dev, IRP);
Case WD_IRP_MN_USER_REQ:
{
Switch (WD_IRPSP_FS_CTL_CODE (STACK))
{
Case WDF_FSCTL_DISMOUNT:
// Here, a Volume is DISMOUNT
RETURN MY_FSCTL_DISMOUNT (DEV, IRP);
}
}
}
WD_SKIP_IO_STACK (IRP);
attached_dev = my_dev_attached (dev);
Return WD_IRP_CALL (Attached_Dev, IRP);
}
You find that you have to start writing two new functions, my_fsctl_mount () and my_fsctl_dism () to process the volume of Mount and DISMOUNT. Obviously, you should generate a device or delete, bind or release it. Soon, you can fully monitor all volumes.
This is dynamically monitored to all volumes of the perfect solution.
If it is above XP, there is a call that can get a file that has been Mount on a file system. But you can't use in 2000. So we didn't use that way. What's more, just getting a volume that mount is not what I want.
There is another MY_FSCTL_LOAD_FS function here. It happened in IRP_MN_LOAD_FILESYS. This function code I only do a little bit: When a file recognizer (see above) decides to load the real file system, a such IRP will generate.
You can now modify your driver, when you pull out the U disk, output debugging information when Volume is loaded. Back to our context:
a. Generate a control device. Of course you must give the control settings to the control settings.
b. Set the Dispatch Functions.
c. Set FAST IO Functions.
d. Write a MY_FS_NOTIFY callback function to bind the fs CDO just activated.
e. Use WDFF_REG_NOTIFY to call registration this callback function.
f. Write the default Dispatch functions.
e. Handle IRP_MJ_FILE_SYSTEM_CONTROL to monitor Volumne's Mount and DISMOUNT.
f. Next step nature is bound Volumne, please follow the decomposition.