文件系统过滤驱动基础知识

文件系统过滤驱动基础知识
一、何谓文件系统过滤驱动?
      文件系统过滤驱动是一种可选的,为文件系统提供具有附加值功能的驱动程序。文件系统过滤驱动是一种核心模式组件,它作为 Windows NT 执行体的一部分运行。
      文件系统过滤驱动可以过滤一个或多个文件系统或文件系统卷的 I/O 操作。按不同的种类划分,文件系统过滤驱动可以分成日志记录、系统监测、数据修改或事件预防几类。通常,以文件系统过滤驱动为核心的应用程序有防毒软件、加密程序、分级存储管理系统等。
二、文件系统过滤驱动并不是设备驱动
      设备驱动是用来控制特定硬件 I/O 设备的软件组件。例如: DVD 存储设备驱动是一个 DVD 驱动。
     
相反,文件系统过滤驱动与一个或多个文件系统协同工作来处理文件 I/O 操作。这些操作包括:创建、打开、关闭、枚举文件和目录;获取和设置文件、目录、卷 的相关信息;向文件中读取或写入数据。另外,文件系统过滤驱动必须支持文件系统特定的功能,例如缓存、锁定、稀疏文件、磁盘配额、压缩、安全、可恢复性、 还原点和卷装载等。
     
下面两部分详细的阐述了文件系统过滤驱动和设备驱动之间的相似点与不同点。
1 、文件系统过滤驱动同设备驱动的相似点:
下列部分描述了 Windows 操作系统中文件系统过滤驱动和设备驱动之间的相似点:
1 )、类似的结构
     
类似于设备驱动,文件系统过滤驱动有着属于自己的 DriverEntry Dispatch I/O 组件例程。文件系统过滤驱动同设备驱动一样调用许多相同的系统核心例程,它们都会过滤发送给它们所关联的设备的 I/O 请求

2 )、类似的功能:
     
文件系统过滤驱动和设备驱动都是 I/O 子系统的组成部分,因此它们都接收和作用于 I/O 请求包( IRP )。
     
类似于设备驱动,文件系统过滤驱动同样可以创建它们自己的 IRP 并将该 IRP 发送到低层驱动。
     
这两种驱动均可以通过注册回调函数来接收多种系统事件的通知。

3 )、其它类似点:
     
同设备驱动类似,文件系统过滤驱动可以接收传入的 I/O 控制码( IOCTLs )。而且,文件系统过滤驱动还可以接收和定义文件系统控制码( FSCTLs
     
同设备驱动类似,文件系统过滤驱动可以被配置为在系统引导过程中加载或者在系统启动过程完成后加载。
2 、文件系统过滤驱动同设备驱动之间的不同点:
     
下例部分描述了文件系统过滤驱动同设备驱动之间的不同点:
     
1 )、无需电源管理
     
由于文件系统过滤驱动并不是真正的设备驱动,而且它们不需要直接控制硬件设备,因此它们并不接收 IRP_MJ_POWER 请求。(电源管理 IRP 将直接发 送到存储设备堆栈中。但是,在非常罕见的情况下,文件系统过滤驱动有可能会影响到电源管理。)由此,文件系统过滤驱动并不注册 IRP_MJ_POWER 关例程,它们也不会调用 PoXxx 例程。
     
2 )、非 WDM
     
文件系统过滤驱动并不是 WDM 驱动程序, WDM 驱动模型仅适用于设备驱动。

     
3 )、没有 AddDevice StartIo 例程
     
由于文件系统过滤驱动并不是设备驱动,而且它们并不直接控制硬件设备,因此它们没有 AddDevice StartIo 例程。

     
4 )、创建不同的设备对象
     
虽然文件系统过滤驱动和设备驱动均需要创建设备对象,但是它们所创建的设备对象的种类和数量都是不同的。
     
设备驱动创建物理和功能设备对象来描述设备。即插即用( PnP )管理器将构建一个设备树来存放所有由设备驱动所创建的设备对象。文件系统过滤驱动所创建的设备对象,并不包含在这个设备树中。
     
文件系统过滤驱动并不创建物理或功能设备对象,它们创建控制设备对象和过滤设备对象。控制设备对象对系统和用户模式应用程序提供过滤驱动的描绘。过滤设备对象执行对指定文件系统或卷的实际过滤工作。文件系统过滤驱动通常创建一个控制设备对象和多个过滤设备对象。

     
5 )、其它不同点:
     
由于文件系统过滤驱动并不是设备驱动,因此他们将不会执行直接内存访问 DMA )。
     
与设备过滤驱动不同,设备过滤驱动可以附加到目标设备功能驱动的上层和下层,文件系统过滤驱动仅能附加到目标文件系统驱动的上层。因此在设备驱动队列中,文件系统仅能进行上层过滤而无法进行下层过滤。
三、安装文件系统过滤驱动
    对于Windows XP和后续操作系统来说,可以通过INI文件或安装应用程序来安装文件系统过滤驱动(对于Windows 2000和更早的操作系统,过滤驱动通常通过服务控制管理器Service Control Manager来进行安装)。
四、初始化文件系统过滤驱动
与设备驱动类似,文件系统过滤驱动也使用 DriverEntry 例程进行初始化工作。在驱动程序加载后,加载驱动相同的组件将通过调用驱动程序的  DriverEntry 例程来对驱动程序进行初始化工作。对于文件系统过滤驱动来说,加载和初始化过滤驱动的系统组件为 I/O 管理器。
    DriverEntry
例程运行于系统线程上下文中,其 IRQL = PASSIVE_LEVEL 。本例程可分页,详细信息参见 MmLockPagableCodeSection
    DriverEntry
例程定义如下:
NTSTATUS
DriverEntry (
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
)
本例程有两个输入参数。第一个参数, DriverObject 为系统在文件系统过滤驱动加载时所创建的驱动对象;第二个参数, RegistryPath 为包含驱动程序注册键路径的 Unicode 字符串。
    
文件系统过滤驱动按如下顺序执行 DriverEntry 例程:

     01
、创建控制设备对象:

    
文件系统过滤驱动的 DriverEntry 例程通常以创建控制设备对象作为该例程的起始。创建控制设备对象的目的在于允许应用程序即使在过滤驱动加载到文件系统或卷设备对象之前也能够直接与过滤驱动进行通信。
  注意: 文件系统也会创建控制设备对象。当文件系统过滤驱动将其自身附加到文件系统之上时(而不是附加到某一特定文件系统卷),过滤驱动同样将其自身附加到文件系统的控制设备对象之上。

    
FileSpy 驱动范例中,控制设备对象按如下方式创建:

RtlInitUnicodeString(&nameString, FILESPY_FULLDEVICE_NAME);
status = IoCreateDevice(
    DriverObject,                                                //DriverObject
    0,                                                           //DeviceExtensionSize
    &nameString,                                                 //DeviceName
    FILE_DEVICE_DISK_FILE_SYSTEM,                                //DeviceType
    FILE_DEVICE_SECURE_OPEN,                                     //DeviceCharacteristics
    FALSE,                                                       //Exclusive
    &gControlDeviceObject);                                      //DeviceObject
 
RtlInitUnicodeString(&linkString, FILESPY_DOSDEVICE_NAME);
status = IoCreateSymbolicLink(&linkString, &nameString);

    
与文件系统不同,文件系统过滤驱动并不是一定要为其控制设备对象命名。如果传递给 DeviceName 参数一个非空 Non-NULL 值, 该值将作为控制设备对象的名称。接下来,在前面的代码范例中 DriverEntry 可以调用 IoCreateSymbolicLink 例程来将该对象的核 心模式名称与应用程序可见的用户模式名称关联到一起(同样可以通过调用 IoRegisterDeviceInterface 来使设备对象对应用程序可 见)。
    
注意: 由于控制设备对象是唯一不会附加到设备堆栈中的设备对象,因此控制设备对象是唯一的可安全命名的设备对象。由此,是否为文件系统过滤驱动的控制设备对象是否命名是可选的。
    
注意: 文件系统的控制设备对象必须命名。过滤设备对象从不命名。
    
参数 DeviceType 代表某种设备类型,其可能的取值均以常量形式定义在 ntifs.h 中,例如:  FILE_DEVICE_DISK_FILE_SYSTEM
    
如果向 DeviceName 传递了一个非空值( Non-NULL ), DeviceCharacteristics 标识必须包括  FILE_DEVICE_SECURE_OPEN 。该标识指示 I/O 管理器对所有发送到控制设备对象的 Open 请求进行安全检测。
    
文件系统过滤驱动在分派例程中识别其自身控制设备对象的有效方式为将设备指针与前期存储的全局控制设备对象指针进行比较。因此 FileSpy 驱动范例将  IoCreateDevice 所返回的设备对象指针存储到了全局变量 gControlDeviceObject 中。

     02
、注册 IRP 分派例程:

    
过滤驱动 DriverEntry 例程中的 DriverObject 参数提供了一个指向过滤驱动的驱动对象的指针。为了注册 I/O 请求包( IRP )的分派例 程,必须为主功能码注册分派例程的入口点。例如: FileSpy 驱动范例按下列方式设置分派例程入口点:

for  (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
    DriverObject->MajorFunction[i] = SpyDispatch;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = SpyCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SpyClose;
DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = SpyFsControl;

    
注意:在上面的 For 循环为每个 IRP 主功能码分派例程都分派了默认的分派例程。这是一个比较好的做法,因为,默认情况下, I/O 管理器完成未知 IRP 返回 STATUS_INVALID_DEVICE_REQUEST 。文件系统过滤驱动在这种方式下不会拒绝未知的 IRP ,这些请求将发送给低层驱动。由于 这个原因,默认分派例程仅向下层传递 IRP

     03
、注册 Fast I/O 分派例程:

    
过滤驱动 DriverEntry 例程的 DriverObject 参数提供了指向过滤驱动驱动对象的指针。
    
为了注册文件系统过滤驱动的 Fast I/O 分派例程,必须分配并初始化 Fast I/O 分派表,向该表中存储 Fast I/O 分派例程,然后将该分派表的地址存储到驱动对象的 FastIoDispatch 成员中。
    
例如: FileSpy 驱动范例按下述方式为 Fast I/O 分派例程设置入口点:
 
RtlZeroMemory(fastIoDispatch, sizeof(FAST_IO_DISPATCH));
fastIoDispatch->SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
fastIoDispatch->FastIoCheckIfPossible = SpyFastIoCheckIfPossible;
fastIoDispatch->FastIoRead = SpyFastIoRead;
fastIoDispatch->FastIoWrite = SpyFastIoWrite;
fastIoDispatch->FastIoQueryBasicInfo = SpyFastIoQueryBasicInfo;
fastIoDispatch->FastIoQueryStandardInfo = SpyFastIoQueryStandardInfo;
fastIoDispatch->FastIoLock = SpyFastIoLock;
fastIoDispatch->FastIoUnlockSingle = SpyFastIoUnlockSingle;
fastIoDispatch->FastIoUnlockAll = SpyFastIoUnlockAll;
fastIoDispatch->FastIoUnlockAllByKey = SpyFastIoUnlockAllByKey;
fastIoDispatch->FastIoDeviceControl = SpyFastIoDeviceControl;
fastIoDispatch->FastIoDetachDevice = SpyFastIoDetachDevice;
fastIoDispatch->FastIoQueryNetworkOpenInfo = SpyFastIoQueryNetworkOpenInfo;
fastIoDispatch->MdlRead = SpyFastIoMdlRead;
fastIoDispatch->MdlReadComplete = SpyFastIoMdlReadComplete;
fastIoDispatch->PrepareMdlWrite = SpyFastIoPrepareMdlWrite;
fastIoDispatch->MdlWriteComplete = SpyFastIoMdlWriteComplete;
fastIoDispatch->FastIoReadCompressed = SpyFastIoReadCompressed;
fastIoDispatch->FastIoWriteCompressed = SpyFastIoWriteCompressed;
fastIoDispatch->MdlReadCompleteCompressed = SpyFastIoMdlReadCompleteCompressed;
fastIoDispatch->MdlWriteCompleteCompressed = SpyFastIoMdlWriteCompleteCompressed;
fastIoDispatch->FastIoQueryOpen = SpyFastIoQueryOpen;
 
DriverObject->FastIoDispatch = fastIoDispatch;

     04
、注册 FsFilter 回调例程:

    FsFilter
通知回调例程在下层文件系统执行某些操作之前或之后调用。如果需要获取更多有关于 FsFilter 回调例程相关信息,可参见 FsRtlRegisterFileSystemFilterCallbacks 例程  
    
为了注册 FsFilter 的通知回调例程必须分配并初始化 FS_FILTER_CALLBACKS 结构体,然后向该结构体中促出 FsFilter 回调例 程,并将存储有 Callbacks parameter FsRtlRegisterFileSystemFilterCallbacks 中。
    
例如: FileSpy 驱动范例按如下方式注册 FsFilter 回调。
 
fsFilterCallbacks.SizeOfFsFilterCallbacks = sizeof(FS_FILTER_CALLBACKS);
fsFilterCallbacks.PreAcquireForSectionSynchronization = SpyPreFsFilterOperation;
fsFilterCallbacks.PostAcquireForSectionSynchronization = SpyPostFsFilterOperation;
fsFilterCallbacks.PreReleaseForSectionSynchronization = SpyPreFsFilterOperation;
fsFilterCallbacks.PostReleaseForSectionSynchronization = SpyPostFsFilterOperation;
fsFilterCallbacks.PreAcquireForCcFlush = SpyPreFsFilterOperation;
fsFilterCallbacks.PostAcquireForCcFlush = SpyPostFsFilterOperation;
fsFilterCallbacks.PreReleaseForCcFlush = SpyPreFsFilterOperation;
fsFilterCallbacks.PostReleaseForCcFlush = SpyPostFsFilterOperation;
fsFilterCallbacks.PreAcquireForModifiedPageWriter = SpyPreFsFilterOperation;
fsFilterCallbacks.PostAcquireForModifiedPageWriter = SpyPostFsFilterOperation;
fsFilterCallbacks.PreReleaseForModifiedPageWriter = SpyPreFsFilterOperation;
fsFilterCallbacks.PostReleaseForModifiedPageWriter = SpyPostFsFilterOperation;
 
status = FsRtlRegisterFileSystemFilterCallbacks(DriverObject, &fsFilterCallbacks);

    05
、执行其它必要的初始化工作:

    
在注册完 IRP Fast I/O 分派例程之后,文件系统过滤驱动的 DriverEntry 例程需要初始化其它该驱动所需的全局变量和数据结构。

    06
、注册回调例程【可选】:

    
过滤驱动可以通过调用 IoRegisterFsRegistrationChange 例程来注册用来侦听在文件系统调用  IoRegisterFileSystem IoUnregisterFileSystem 注册或卸载自身时所触发事件的回调例程。过滤驱动通过注册这个 回调函数来发觉新的文件系统加载事件,然后,过滤驱动将自身附加到这个新的文件系统之上。
    
注意: 文件系统过滤驱动不可能调用 IoRegisterFileSystem IoUnregisterFileSystem 例程。这两个例程都是专为文件系统提供服务的。
    
过滤驱动并不是在调用 IoRegisterFsRegistrationChange 时加载到卷之上,它必须在侦测到卷以后才能进行加载(例如,通过一个用户模式应用程序)。注意: 滤驱动使用该例程来获得在卷装在后立即附加到该卷之上的能力。使用该例程时,并不能保证过滤驱动将直接附加到卷设备对象之上。其余部分未翻译,如下:  But it does ensure that such a filter attaches before (and thus below) any filter that instead waits for a command from a user-mode application, because filters can attach only at the top of the current file system volume device stack.

    07
、存储注册表路径字符串拷贝【可选】:

    
注意: 本步骤是过滤驱动在执行 DriverEntry 例程之后,需要使用注册表路径时所必须的。
    
其余部分未翻译,如下: Save a copy of the RegistryPath string that was passed as input to DriverEntry. This parameter points to a counted Unicode string that specifies a path to the driver's registry key,
/Registry/Machine/System/CurrentControlSet/Services/DriverName, where
DriverName is the name of the driver. If the RegistryPath string will be needed later, DriverEntry must save a copy of it, not just a pointer to it, because the pointer is no longer valid after the DriverEntry routine returns. 

    08
、返回状态:

     文件系统过滤驱动的 DriverEntry 例程通常会返回 STATUS_SUCCESS 。然而,如果驱动初始化失败, DriverEntry 会向返回一个适当的状态值。
    
如果 DriverEntry 例程返回了一个指示未成功的状态值,系统将卸载该驱动。因此, DriverEntry 例程必须在返回错误代码之前释放那些自己所分配的内存和所获取的诸如设备对象之类系统资源。
 
五、附加过滤驱动到文件系统或卷之上
文件系统过滤驱动将其自身附加到一个或多个已装载卷之上,并过滤发送给这些卷的 I/O 操作。下面将以 Windows Driver Kit  WDK )中的范例来说明通常会采用的两种方式:
·          文件系统过滤驱动可以附加到终 端用户指定需要过滤的卷,比如键入卷的驱动符。用户命令将传递给过滤驱动一个私有的 IRP_MJ_DEVICE_CONTROL 请求。 FileSpy 驱动 范例在全局变量 gFileSpyAttachMode 设置为 FILESPY_ATTACH_ON_DEMAND 时,采用这种方式(默认情况下,  gFileSpyAttachMode 将设置为 FILESPY_ATTACH_ALL_VOLUMES )。
·          文件系统过滤驱 动可以附加到一个或多个文件系统驱动之上,监听  IRP_MJ_FILE_SYSTEM_CONTROL IRP_MN_MOUNT_VOLUME 请求,并附加到文件系统所装载的卷之上。 SFilter  就驱动范例使用这种方式。 FileSpy 驱动范例在全局变量 gFileSpyAttachMode 被设置为  FILESPY_ATTACH_ALL_VOLUMES (默认值)采取这种方式。
      注意: 通常需要假定卷和文件系统驱动的关系为一对多,而不是一对一。这是由于某些高级存储功能,例如动态卷和卷装载点等所造成的。
    
注意: 要将定文件系统会一直以同步方式处理 IRP_MN_MOUNT_VOLUME 请求。例如,辅助存储器可能异步的进行装载。因此,过滤驱动需要在 Mount Completion 例程中传递 PendingReturned 标识。如果需要获取更多信息,可查阅 DDK 在线文档的 “PendingReturned Flag” 部分。  
    
文件系统过滤驱动可以附加并过滤文件系统卷。但是它们无法直接附加到诸如磁盘驱动或分区等存储设备之上,同样他们也无法附加到单独的目录或文件之上。
     
如果需要获取更多信息,可参见下面章节:

    01
、创建过滤设备对象

    
调用 IoCreateDevice 例程来创建用来附加到卷或文件系统堆栈上的过滤设备对象,在 FileSpy 范例中,以下述方式进行该工作:

status = IoCreateDevice(
    gFileSpyDriverObject,     //DriverObject
    sizeof(FILESPY_DEVICE_EXTENSION),     //DeviceExtensionSize
    NULL,           //DeviceName
    DeviceObject->DeviceType,     //DeviceType
    0,            //DeviceCharacteristics
    FALSE,          //Exclusive
    &newDeviceObject);        //DeviceObject


    
在上面的代码片段中, DeviceObject 为指向过滤驱动所需要附加到的目标设备的设备对象,而 newDeviceObject 为指向过滤驱动自身的设备对象的指针。
    
为了为过滤设备对象的设备扩展数据结构体分配存储空间,因此需要将 DeviceExtensionSize 参数设置为 sizeof (FILESPY_DEVICE_EXTENSION) 。新创建的过滤设备对象的设备扩展成员将设置为指向该数据结构的指针。文件系统过滤驱动通常为每个 过滤设备对象定义并分配设备扩展。设备扩展的数量和结构均由驱动指定。然而,在 MS Windows XP 以及后续操作系统之上,过滤驱动所定义的设备扩展结构 DEVICE_EXTENSION 至少需要包含下述成员: PDEVICE_OBJECT AttachedToDeviceObject
    
在上面调用 IoCreateDevice 时,由于过滤设备对象并没有命名,因此将 DeviceName 参数设置为 NULL 。由于过滤设备对象需要附加到文件系统或卷设备堆栈之上,因此,为过滤设备对象分配一个名字将造成系统安全漏洞。
    DeviceType
参数必须始终设置为过滤设备对象所附加到的目标(文件系统或过滤)设备对象。按照此方法传递设备类型是非常重要的,这是因为它将由 I/O Manager 使用,并传回给应用程序。
    
注意: 文件系统和文件系统过滤驱动从来不会将 DeviceType 参数设置为 FILE_DEVICE_FILE_SYSTEM 。该值并不是一个有效参数值( FILE_DEVICE_FILE_SYSTEM 常量仅在定义 FSCTL 时使用)。
    DeviceType
参数非常重要的其它原因是,许多过滤驱动仅仅附加到某些特定的文件系统之上。例如,一个特殊的过滤驱动将会附加到本地磁盘文件系统之 上,而并不附加到 CD-ROM 文件系统或远程文件系统之上。这些过滤驱动测试最文件系统或卷设备堆栈中最高层设备对象的 DeviceType 参数。在大多 数情况下,最高层设备对象为一个过滤设备对象。这就是为什么过滤驱动的 Device Type 参数需要同下层文件系统或卷设备对象的该参数一致的原因。


    02
、将过滤设备对象附加到目标设备对象之上

    
通过调用 IoAttachDeviceToDeviceStackSafe 来将过滤设备对象附加到目标文件系统或卷的过滤设备堆栈之中。

devExt = filespyDeviceObject->DeviceExtension;
status = IoAttachDeviceToDeviceStackSafe(
     filespyDeviceObject,       //SourceDevice
     DeviceObject,        //TargetDevice
     &devext->AttachedToDeviceObject);  //AttachedToDeviceObject


    
注意: 在有其它过滤驱动已经附加到目标设备对象之上时, AttachedToDeviceObject 输出参数接收到的设备对象指针可以与 TargeDevice 不同。

     通过名称附加到文件系统之上
     每种文件系统需要创建一个或多个已命名控制设备对象。如果需要直接附加到文件系统之上,文件系统过滤驱动需要传递给  IoGetDeviceObjectPointer 传递该特定文件系统控制设备对象的名称来获取该设备对象的指针。下列代码范例显示了如何获取 RAW 文件 系统的两个控制设备对象中的一个的方法:

RtlInitUnicodeString(&nameString, L"//Device//RawDisk");
status = IoGetDeviceObjectPointer(
      &nameString,        //ObjectName
      FILE_READ_ATTRIBUTES,     //DesiredAccess
      &fileObject,        //FileObject
      &rawDeviceObject);      //DeviceObject
if (NT_SUCCESS(status)) {
      ObDereferenceObject(fileObject);
}

    
如果调用 IoGetDeviceObjectPointer 成功,文件系统过滤驱动调用 IoAttachDeviceToDeviceStackSafe 方法来附加到前述方法返回的控制设备对象之上。

    
注意:除了控制设备对象指针( rawDeviceObject )以外, IoGetDeviceObjectPointer 还会返回一个指向向用户模式应用表现该控制设备对象的文件对象的指针( fileObject )。在上述代码范例中,并不需要文件对象,因此通过调用 ObDereferenceObject 关闭了该文件对象。下面没有翻译: It is important to note that decrementing the reference count on the file object returned by IoGetDeviceObjectPointer causes the reference count on the device object to be decremented as well. Thus the fileObject and rawDeviceObject pointers should both be considered invalid after the above call to ObDereferenceObject, unless the reference count on the device object is incremented by an additional call to ObReferenceObject before ObDereferenceObject is called for the file object. 


    03
、传递 DO_BUFFERED_IO DO_DIRECT_IO 标识

    
在过滤设备对象成功的附加到文件系统或卷之上以后,下面没有翻译: always be sure to set or clear the DO_BUFFERED_IO and DO_DIRECT_IO flags as needed so that they match the values of the next-lower device object on the driver stack. (For more information about these flags, see Methods for Accessing Data Buffers.) In the FileSpy sample, this is done as follows: 

if (FlagOn( DeviceObject->Flags, DO_BUFFERED_IO )) 
{
    SetFlag( filespyDeviceObject->Flags, DO_BUFFERED_IO );
}
if (FlagOn( DeviceObject->Flags, DO_DIRECT_IO )) 
{
    SetFlag( filespyDeviceObject->Flags, DO_DIRECT_IO );
}


    
在上述代码片段中, DeviceObject 为一个过滤设备对象附加到的设备对象的指针,  filespyDeviceObject 为指向过滤设备对象自身的指针。


    04
、传递 FILE_DEVICE_SECURE_OPEN 标识

    
在过滤设备对象成功附加到文件系统(并非卷)设备对象之上以后,下面没有翻译: always be sure to set the FILE_DEVICE_SECURE_OPEN flag on the filter device object as needed to so that it matches the value of the next-lower device object on the driver stack. (For more information about this flag, see Specifying Device Characteristics in the Kernel Architecture Design Guide and DEVICE_OBJECT in the Kernel Reference.) FileSpy 驱动范例中,实现方式如下:

if (FlagOn( DeviceObject->Characteristics, FILE_DEVICE_SECURE_OPEN )) 
{
    SetFlag( filespyDeviceObject->Characteristics, FILE_DEVICE_SECURE_OPEN );
}


    
在上述代码片段中, DeviceObject 为一个过滤设备对象附加到的设备对象的指针,  filespyDeviceObject 为指向过滤设备对象自身的指针。


    05
、清除 DO_DEVICE_INITIALIZING 标记:

    
在过滤设备对象附加到文件系统或卷之上以后,,需要确保清除过滤设备对象的 DO_DEVICE_INITIALIZING 标识。在 FileSpy 驱动范例中,以如下方式实现:

ClearFlag(NewDeviceObject->Flags, DO_DEVICE_INITIALIZING);


    
在过滤设备对象创建以后, IoCreateDevice 为设备对象设置 DO_DEVICE_INITIALIZING 标识。在过滤驱动成功进行附加以后,这个标识必须被清除掉。注意:如果本标识没有被清除,其它过滤驱动将无法再次附加到该过滤链中,因为,此时调用 IoAttachDeviceToDeviceStackSafe 将失败。

    
注意: DriverEntry 例程中创建的设备对象,并不需要必须清除 DO_DEVICE_INITIALIZING 标识,这是因为这个工作将会由 I/O 管理器自动完成。然而,如果创建了其它设备对象,则需要进行该清除工作。
 

猜你喜欢

转载自blog.csdn.net/snowfoxmonitor/article/details/80549795