(转载)Windows文件系统过滤驱动开发教程 联系客服

发布时间 : 星期五 文章(转载)Windows文件系统过滤驱动开发教程更新完毕开始阅读1b486c1614791711cc79175f

FAT和CDFS以及被WinSock使用的传输驱动AFD。

任何驱动都可以注册一系列Fast I/O接口,但使用起来还有很大的限制—在这些接口被调之前需要满足合适的条件。例如,读操作和写操作的Fast I/O接口只有当Windows NT cache管理器保留了文件的信息时才被调用。我们在接下的论述中将会讲述这些限制。

当然,Windows NT的Fast I/O最让人郁闷的是关于它的资料很少,即使文件系统开发包也没有讲述Fast I/O是如何工作和怎样来使用Fast I/O。 原理

提供了Fast I/O是非常方便的---许多I/O操作可以对相同的数据进行重复操作。例如和许多流行的操作系统一样,Windows NT用虚拟内存集成了文件系统的缓冲,这样的系统无论是在使用上还是在感觉上都很有效率。

这种集成的另一原因是Windows NT支持内存映射文件。支持读写和内存映射相同的数据要么需要代价很高的cache一致性策略,要么使用NT的策略---将所有数据存储在虚拟内存中。这样,即便是两个程序用不同的技术访问相同的数据,也确保了数据的一致性。

这种紧密的集成意味着无论是读还是写都经常是对cache中的数据来操作。在查找过程中,这种策略用来调用一个特殊的程序,此程序将虚拟机(VM)的cache中的数据移到用户内存中,反之亦然。这样就避免了生成IRP,并且不需要请求底层的驱动了。这就是Fast I/O操作的基本功能。

一旦在程序中定义了Fast I/O读写接口,那么同时还需要进行一步添加其它的通用Fast I/O操作到Fast I/O链中,Fast I/O链中有13个接口(在NT3.51中)。在我们接下来要讲的各接口过程中,你会明显地发现各接口是互相关联的。这些接口包含在FAST_IO_DISPATCH结构中,此结构在ntddk.h中有定义。这个结构的第一个元素表示结构的大小,为以后在结构添加新接口提供了一种向上兼容的机制。

I/O管理器和Fast I/O

I/O管理器在必要的时候负责调用Fast I/O接口。Fast I/O调用返回TRUE或FALSE表示Fast I/O操作是否完成。如果Fast I/O没有被完成或无效,则会产生一个IPR并发送到上层驱动,但是FAST_IO_DISPATCH结构中最近的三个接口却不是这样的,它们为I/O管理员提供了不同的服务,接下来我们将讨论它们。

FastIoCheckIfPossible

这是在FAST_IO_DISPATCH结构中第一个调用的,仅被用来作为一般的文件系统库操作(以FsRtl开头的函数)。原型如下:

typedef BOOLEAN (*PFAST_IO_CHECK_IF_POSSIBLE)(

IN Struct _FILE_OBJECT *FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey,

IN BOOLEAN CheckForReadOperation, OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject );

这个函数被FsRtl库中提供的通用的Fast I/O函数调用,仅用在读写操作中,以获取使用了通用文件缓存管理系统中的读和写是否能在文件缓存中被响应(由参数CheckForReadOperation的值决定)。注意,除了这个函数没有分配任何数据空间外其它参数和读写的Fast I/O接口参数相似。

FastIoRead and FastIoWrite

当对一个已经分配了有效数据缓存的文件进行读请求时,这个函数被I/O管理器调用。原型如下:

typedef BOOLEAN (*PFAST_IO_READ)( IN struct _FILE_OBJECT *FileObject, IN PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey, OUT PVOID Buffer,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject );

正如前面所讲的,基本的调用参数和FastIoCheckIfPossible相似,就是多了一个必要的数据缓存参数。要保证所有Fast I/O调用接口的参数有效,例如上面的Buffer指针,必需是有效的并且在读线程的上下文中能使用此指针。

Fast I/O函数可以完成以下两件事情之一:第一,当操作完成时设置IoStatus的返回值并给I/O管理器返回TRUE,这时I/O管理器会完成对应的I/O操作。第二,直接返回FALSE给I/O管理器使其构造一个IRP从而调用标准的分派例程。

要注意的是返回TRUE并不能保证数据被传输了。例如,一个从文件结束处开始的读请求会设置IoStatus.Results为STATUS_END_OF_FILE,并且没有数据被复制。但是当一个读操作读到了文件的结尾,这时会将IoStatus.Results设置为STATUS_END_OF_FILE,返回TRUE,并且将读到的数据复制到Buffer里。

同样,返回FALSE并不能说明所有的数据没有被处理。例如,当然很少有这种可能,数据已经被成功处理了,但抛出一个I/O错误,或者内存不可访问。

以上任何一种情况出现都会导致不良影响。例如,从缓存读数据时,可能要读的数据并不在缓存中,这时会导致一个页错误,从而会请求文件系统来处理这个页面错误。

Fast I/O的写函数与读函数不同之处仅仅在于Buffer参数一个是输入型而不是输出型的,其它的基本操作很相似。当然,一些错误可能不同---介质已经满,需要分配新页等等。

FastIoQueryBasicInfo and FastIoQueryStandardInfo

这两个操作为标准的NtQueryInformationFile API操作提供了支持,而FastIoQueryBasicInfo也经常被用来处理NtCreateFile的特定操作。文件的基本属性包括创建时间、访问时间和修改时间,以及隐藏、文件夹或其它属性等。文件的标准属性包括文件占用的空间、文件的大小、文件的硬连接号、被请求删除的标志,是否是文件夹的标识。

由于这些信息经常在缓存中,所以它是FAST I/O操作的最佳候选。其实许多程序用这种方法来获取文件基本的信息,因为这种方法提高了操作的效率和程序的性能,如文件管理器程序(winfile.exe)。

这两个Fast I/O例程有相同的接口:

typedef BOOLEAN (*PFAST_IO_QUERY_ABSIC_INFO)( IN struct _FILE_OBJECT *FileObject, IN BOOLEAN Wait,

OUT PFILE_BASIC_INFORMATION Buffer, OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject);

Wait参数表示调用者是否阻塞以等待获取信息的返回,如果设置为FALSE,则调用要么立刻完成,要么返回FALSE,如果返回FALSE,则会生成一个包含整个操作必要的上下文件的IRP。有趣的是在NT3.51中当Wait设置为FALSE时这两个例程将不被调用,当然这在以后的版本中会修改。

这两个例程一但被调用,它们会查询文件对象第一次打开时保存的信息,这些信息也可能被实时变化,例如,文件的最近访问时间属性会被文件系统设置的当前的系统时间,当然设置这些属性取决于文件系统的实现。

FastIoLock,FastIoUnLockSingle,FastIoUnLockAll,and FastIoUnLockAllByKey 这些例程被用来控制特殊文件的加锁状态。对文件的加锁以字节为单位,所以可以对文件的多个字节进行加密。标准的NT文件系统使用文件系统运行时开发包(FsRtl函数)所提供的通用代码来验证锁权限并存储文件的加锁范围。锁状态通过调用NT API函数NtLockFile和NtUnLockFile来控制。

在Windows NT中有两种锁,一种是排它锁,是写锁,说明加锁的地放要进行修改;另一种

是共享锁,是读锁,说明加锁的地放用来的读的。多个共享锁在重叠操作中可以被授权,并且一直保存到释放为止。将各种加锁的信息存储起来在访问这些信息的时候会提高速度。 FastIoLock的原型:

Typedef BOOLEAN (*PFAST_IO_LOCK)( IN struct _FILE_OJBECT *FileObject, IN PLARGE_INTEGER FileOffset, IN PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key,

BOOLEAN FailImmediately, BOOLEAN ExclusiveLock,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject );

FileOffset 和Length参数对应加锁的范围,ProcessId标识加锁的进程,如果进行退出,锁将会清除。Key参数提供一个非透明的值,用来关联多个锁,例如调用FastIoUnLockAllByKey可以快速访问多个锁。FailImmediately用来标识当锁无效时是立刻返回失败还是阻塞直到锁可用。对于FsRtl函数,如果是无效的锁则忽略FailImmediately参数,函数返回FALSE。ExclusiveLock参数用来标识是排它锁还是共享锁。

FastUnlockSingle例程被用来释放对文件的加锁,原型如下: Typedef BOOLEAN (*PFAST_IO_UNLOCK_SINGLE)( IN struct _FILE_OBJECT *FileObject, IN PLARGE_INTEGER FileOffset, IN PLARGE_INTEGER Length, PEPROCESS ProcessId, ULONG Key,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject );

对大多文件系统来说,如果文件没有加锁,此例程总是返回TRUE,即使朝无效的锁,操作也会完成,因为用IRP来操作也同样会产生相同的错误。

如果这个解锁操作成功,那么FileOffset,Length,ProcessId,和Key必须和相应的锁信息匹配,否则操作会返回错误STATUS_RANGE_NOT_LOCKED。FastIoUnlockAll例程用来释放特殊文件所有的锁,函数原型如下:

typedef BOOLEAN(*PFAST_IO_UNLOCK_ALL)( IN struct _FILE_OBJECT *FileObject, PEPROCESS ProcessId,

OUT PIO_STATUS_BLOCK IoStatus,

IN struct _DEVICE_OBJECT *DeviceObject );

在这种情况下,Fast I/O例程查找进程ProcessId所操作的文件所有的锁,并删除,无论是排它锁还是共享锁。这个例程常用在当系统由于关掉程序或终止程序而调用NtCloseFile时。