📄 微过滤器驱动开发指南 之四.txt
字号:
QueryMethod应该是以下之一:
FLT_FILE_NAME_QUERY_DEFAULT: 搜索一个名字的时候,管理器会首先找暂存的名字。然后再询问文件系统。
FLT_FILE_NAME_QUERY_CACHE_ONLY: 仅仅在暂存中找。如果失败,返回STATUS_FLT_NAME_CACHE_MISS.
FLT_FILE_NAME_QUERY_FILE_SYSTEM_ONLY:仅仅询问文件系统,不会从暂存中去寻找这个名字。
名字在最后一个参数中返回,FileNameInformation.这个结构是一组共享缓冲的Unicode字符串。不同的字符串表明不名字中不同的部分.
typedef struct _FLT_FILE_NAME_INFORMATION {
USHORT Size;
FLT_FILE_NAME_FORMAT Format;
FLT_FILE_NAME_PARSED_FLAGS NamesParsed;
UNICODE_STRING Name;
UNICODE_STRING Volume;
UNICODE_STRING Share;
UNICODE_STRING Extension;
UNICODE_STRING Stream;
UNICODE_STRING FinalComponent;
UNICODE_STRING ParentDir;
} FLT_FILE_NAME_INFORMATION, *PFLT_FILE_NAME_INFORMATION;
当一个文件名信息结构从FltGetFileNameInforamtion()返回,name,Volume,Share(用于远程文件)会被解析出来。如果一个微过滤器需要其他的名字信息,它应该调用FltParseFileNameInformation().
运行在DPC或者更低级的中断级别的时候,微过滤器可以在IO过程中任何地方调用FltGetFileNameInformation().当它在一个可能导致死锁的情况下请求(比如处理分页交换的时候),如果在暂存中没有找到名字或者指定了必须从文件系统读取那么调用会失败。
即使没有回调数据(Callback Data)存在,仅仅知道文件对象(FileObject)的时候,微过滤器也可以调用FltGetFileNameInformationUnsafe()来获得一个文件名。不过过滤器必须知道当前向文件系统询问这个名字是安全的。这个调用不能像FltGetFileNameInformation()那样检查是否导致死活。微过滤器必须自己保证它。
当一个名字使用完毕,应该调用以下的例程来释放:
FLTAPI
FltReleaseFileNameInformation (
IN PFLT_FILE_NAME_INFORMATION FileNameInformation
);
过滤管理器的名字暂存对微过滤查找对象的名字来说效率足够了。名字暂存机制也管理了由于重新命名而无效的名字。维护一个名字暂存空间的复杂逻辑对微过滤器们而言是透明的。但是微过滤器对于无效的名字并不是完全不需要关系。当一个重命名发生了,过滤管理器清理所有的受影响的暂存文件名。但是微过滤器可能引用过一个过时的文件名。当这些引用全部被释放的时候,过时的文件名信息结构才会被真的释放掉。如果一个微过滤器请求询问一个已经被重新命名的对象,将尽可能的返回新的名字。
11.2 文件名的附加支持
过滤管理器也提供了一个编程接口来帮助微过滤器获得一个改名或生成硬连接的操作的目的名:
NTSTATUS
FltGetDestinationFileNameInformation (
IN PFLT_INSTANCE Instance,
IN PFILE_OBJECT FileObject,
IN HANDLE RootDirectory OPTIONAL,
IN PWSTR FileName,
IN ULONG FileNameLength,
IN FLT_FILE_NAME_FORMAT NameFormat,
IN FLT_FILE_NAME_QUERY_METHOD QueryMethod,
OUT PFLT_FILE_NAME_INFORMATION *RetFileNameInformation
);
这个接口只能在IRP_MJ_SET_INFORMATION的预操作回调中调用,包括FileSetNameInformation和FileSetLinkInformation。调用者从这个操作中获得参数,这个调用会返回一个FLT_FILE_NAME_INFORMTION结构,包含了目标文件名在其中。与使用FltGetFileNameInformation()相同,这个调用返回的名字使用完毕之后,应该调用FltReleaseFileNameInformation()来释放。
命名隧道(Name Tunneling)是过滤器的另一个困绕之处。过滤管理器提供了一个编程接口来获得一个命名管道需要的新名字:
NTSTATUS
FltGetTunneledName (
IN PFLT_CALLBACK_DATA CallbackData,
IN PFLT_FILE_NAME_INFORMATION FileNameInformation,
OUT PFLT_FILE_NAME_INFORMATION *RetTunneledFileNameInformation
);
命名隧道仅仅影响获取文件名采用通常格式的微过滤器。如果一个微过滤器在它的一个预操作需要一个通常格式的文件名,这个预操作回调的来源是CREATE,改名,或生成硬连接的话,它应该在后操作回调中调用FltGetTunneledName()来确认这个名字是否被操作系统命名隧道所改过。如果命名隧道确实出现了,那么RetTunneledFileNameInformation中回返回一个新的文件名字信息结构。微过滤器必须用这个新的名字,并且处理完毕后必须用FltReleaseFileName()来释放它。
(译者注:什么是命名隧道(Name Tunneling)
长文件名出现之后,旧的16位应用程序随时可能破坏掉长文件名。为此出现了所谓的命名隧道概念。现在假设一个16位的程序比如文字处理程序把当前版本的文挡维护在一个临时文件中。当用户修改这个文件,原始的文件就被删除了,临时文件被改为原来的文件名。
如果原始文件有一个长文件名,但是临时文件仅仅有短文件名,那么当旧的文件被删除,名字也跟着丢失了。因此当命名隧道起作用的时候,文件系统记住了每个被删除的文件名一段时间(比如15秒),如果一个短文件名的文件生成了,刚好和被记忆的长文件名配套,那么短文件名自动改名为长文件名。这就是命名隧道概念。
想自己尝试一下:首先,在一个空文件夹中生成一个文件 longfilename.然后删除它。在生成一个文件longfi~1,然后输入dir /x,这时你发现,longfilename又出现了!)
11.3 名字提供接口
如果一个过滤器打算提供一个途径来改变名字空间,他必须注册三个附加的回调给过滤管理器。这些回调允许过滤器作为名字的提供者。过滤器将有责任对上层发来的FltGetFilenameInformation或者FltGetDestinationFileName返回FLT_FILE_NAME_INFORMATION结构,并填写其中名字的内容。而且这样的微过滤器还可以告知过滤管理器,他们所返回的名字要不要被暂存。
作为一个名字提供者,过滤器必须可以返回一个指定的文件对象(file object)的开放格式的名字。如果确定了通常名字格式,过滤管理器将重新遍历这个名字的所有部分并调用名字提供者的徽调来展开这些部分为一个通常格式的文件名。在展开一个路径的所有部分的过程中,过滤器的名字通常化例程可能被调用不止一次。因此这个过程中允许过滤器传入一个上下文。所有的过程结束后,如果调用返回了,过滤器会被要求清理这个上下文。
当一个过滤器作为名字提供者必须提供一个名字的时候,它的PFLT_GENERATE_FILE_NAME例程会被调用。你能在参数中得到文件对象,过滤器的实例,同时回调数据描述发生的操作。此外还有名字请求选项。那么过滤器必须在这中间生成名字:
typedef NTSTATUS
(*PFLT_GENERATE_FILE_NAME) (
IN PFLT_INSTANCE Instance,
IN PFILE_OBJECT FileObject,
IN PFLT_CALLBACK_DATA CallbackData OPTIONAL,
IN ULONG NameOptions,
OUT PBOOLEAN CacheFileNameInformation,
OUT PFLT_NAME_CONTROL FileName
);
typedef NTSTATUS
(*PFLT_NORMALIZE_NAME_COMPONENT) (
IN PFLT_INSTANCE Instance,
IN CONST PUNICODE_STRING ParentDirectory,
IN USHORT VolumeNameLength,
IN CONST PUNICODE_STRING Component,
IN OUT PFILE_NAMES_INFORMATION ExpandComponentName,
IN ULONG ExpandComponentNameLength,
IN OUT PVOID *NormalizationContext
);
typedef VOID
(*PFLT_NORMALIZE_CONTEXT_CLEANUP) (
IN PVOID *NormalizationContext
);
如果一个过滤器希望让所有自己提供的名字的暂存失效,他可以调用以下的接口。在此之前它可能已经提供了一些FLT_FILE_NAME_INFORMATION结构,而且其他过滤器可能刚好还在使用。那么这些FLT_FILE_NAME_INFORMATION结构只有当引用数降到0(已经没有人使用了),才会自己释放掉。
NTSTATUS
FltPurgeFileNameInformationCache (
IN PFLT_INSTANCE Instance,
IN PFILE_OBJECT FileObject OPTIONAL
);
为了确保这样一个过滤可以卸载,过滤管理器有责任管理这些过滤器提供的文件名的暂存和释放。
一些FltGetFileNameInformation()调用FltGetDestinationFileName()调用基于一些过滤器提供的名字。过滤管理器将负责初始化这些FLT_FILE_NAME_INFORMATION结构。
为了提高效率,过滤管理器往名字提供者的PFLT_GENERATE_FILE_NAME回调传入一个缓冲区。这个缓冲是一个FLT_NAME_CONTROL所包装的UNICODE_STRING.这个结构中包含一些公有的或者私有的信息。在一个过滤器试图填充这个缓冲之前,应该先检查缓冲是否足够的大:
NTSTATUS
FltCheckAndGrowNameControl (
IN OUT PFLT_NAME_CONTROL NameCtrl,
IN USHORT NewSize
);
如果这个调用返回STATUS_SUCCESS,说明FLT_NAME_CONTROL结构足够容纳从名字提供者返回的名字了。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -