>> Seeing so many posts about on-the-fly encryption/decryption filter driver
> workflow.
The actual workflow is something better to be left for the actual
implementation, for its highly dependant
bu design. There are more than one way to design such a file system filter
driver, some very simple,
some very complex. Also the design will impact on many practical solutions
and requirments during
implementation.
> If you have some links, docs or a piece of advise, you’re always welcome

Unfortunately, there are no docs , or links or samples or whatever on this
subject.
Since I have nothing better to do in this morning, lemme try to summarize
some questions,
if I make any mistakes , feel free to point them out.
1.How do I identify paging IO ? What are the sources of paging IO ? How do
IRP_PAGING_IO flag modify completion behaviour ? Why cant I
modify the data in the original buffers for a IRP_MJ_WRITE paging IO
operation ? What about an IRP_MJ_READ paging IO ? Can I query the
file system for (your advertising place here) during a paging IO operation?
Why the hell do I see IRP_NOCACHE IO for my cached files ?
Paging IO can be identified by looking at Irp->Flags, for IRP_NOCACHE
and IRP_PAGING_IO flags set. Synchronous paging IO
requests also have IRP_SYNCHRONOUS_PAGING_IO set. The IRP_NOCACHE flag
simply tells the file system that the request
should NOT be satisfied from looking up data in system cache. IRP_PAGING_IO
has a dual role. First, it tells file system that the
request is paging IO. While normal IO is considered illegal on a file object
for which the file system received a IRP_MJ_CLEANUP
operation (Handle count for that file object reached 0), paging IO is. An
important thing to remember, should you build your
own IRPs in the paging IO path. Second it will drasticly modify the IO
completion behaviour. More on this later.
Files which are likely to be subject to paging IO requests:
System paging files.
Any file which is currently used as memory mapped file. This include
executable images, and all cached files. The Cc
manager uses section objects and mapped views to implement caching on file
streams.
Hibernation files
Paging files are easily identified in a filter driver at IRP_MJ_CREATE time
by looking in IrpSp->Flags for
SL_OPEN_PAGING_FILE set. This will instruct the file system to allocate all
in memory representation of the file
stream from nonpaged memory pool. This is an important issue, because the
system cannot afford a recursive page
fault in paging IO path for PAGING files. For a file system filter driver,
since usually paging file path
is the same as the genral paging IO path, this means that none of the
routines which are in paging IO path
can be pagable.
Note that hibernation files are also created as paging files. In Windows XP+
one can differentiate between a paging
file and a hibernation file by using FsRtlIsPagingFile() routine.
Sources of paging IO are:
IRP_MJ_WRITE:
The modified page writer and mapped writer threads –> uses
asynchronous write paging IO
flags set in Irp IRP_NO_CACHE| IRP_PAGING_IO
The lazy writer component of Cc manager –> uses synchronous write
paging IO
flags set in Irp IRP_NO_CACHE| IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO
IRP_MJ_READ:
A inpage operation on behalf of Mm, as the result of a page fault. ->
uses synchronous read
flags set in IRP: IRP_NOCACHE| IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO
The read ahead component of Cc manager: it will bring memory in by
touching the pages subject to
inpage. If the memory is not present, a page fault will occur. The Mm will
kick in, briniging desired pages in memory
from secondary storage.
Non- Mm/Cc requests, such as retrieving a crash dump from the main
system pagefile.
IRP_MJ_SET_INFORMATION / IRP_MJ_QUERY_INFORMATION === set / query size
It is important to note that IRP_PAGING_IO flag will change the behaviour of
IO completion. The significant
changes are the folowing(important to keep in mind if you roll your own
paging IO IRPs):
-
Normally , MDLs associated with a IRP are freed during IoCompletion
processing. This is not true
for paging IO. Usually those MDL are owned by the Mm , and not by Io manager
as usual, so they are left
there for the Mm to deal with the.
-
for a synchronous paging IO the completion will copy the operation status
into the callers
IO_STATUS_BLOCK, signal the event on which the caller blocks, and free the
IRP. For an asynchronous paging
IO an APC is delivered to the thread which requested the IO operation.
For write paging IO operation, one must not use the original MDL and buffer
to encrypt / change the data in place.
By doing so you will make imeddiately any changes visible in system cache,
or general system memory. A
page which is in transition can be reclaimed by a process and added to the
process working set. This will cause
inconsistent information. If you dont want your changes to be visible
imediatley, and usually you dont want, you must
use some kind of buffering. In the event you use roll your own IRP for this
purpouse, remember that the current
request can be for a a file object which already received a IRP_MJ_CLEANUP.
So build your IRP acordingly.
In the event you reuse the original Irp, and replace Irp->MdlAddress with
your own MDL, describing your buffer,
remember to always update the Irp->UserBuffer using
MmGetMdlVirtualAddress(). Upon completion, restore original
values in IRP.
For read paging IO operations, you can, however, read the data into the
original provided buffer, and decrypt
in place. It is safe to do so, because the target buffer is considered
invalid by Mm untill the inpage opeartion
is complete. Those pages are not yet part of any working set, beeit system
working set or process working set.
If you wonder how this operates: The pages subject to a inpage operation are
marked as “in transition” && a flag is
set on them that indicates that a inpage operation is in progress. When the
inpage operation is complete with
success, the pages are added to the working sets. If another thread touches
the same memory range while the inpage
operation is not completed yet, the Mm will see this operation as a
transition page fault and that a inpage is pending ,
(remember, the pfn was marked as in transition and a flag to indicate a read
operation is also set) and will block the
thread untill the inpage operation is complete. The Mm will not issue a
inpage operation again in this situation.
One not about memory mapped files. A section object can be backed up on disk
either by the original file which is
memory mapped, either by a pagefile. IO on memory mapped files is performed
by writting/ reading data directly
into the virtual memory representing a mapped view of a section object.
Actual disk IO will take place only
as paging IO. The target file stream can be either a system pagefile, either
the file itself on disk, depending
on how the section object is backed up.
When processing a paging IO you are somewhow limited as what operation you
can request from the file system.
indeed both Cc manager and MmManager might grab(or not =)) (I can describe
actual algorithms if anyone
is interested) FS synchronization objects for the current file stream. Thus,
you might deadlock the system.
The actual limits of what or what you cant do are dependant of underlieing
file system implementation.
- How do I disable read ahead / write behind for a file stream ? How can I
flush cache of a file stream
Will any write operations resulted from a section / cache flush result in
anything else than paging IO ?
Will read ahead result in anything else than paging IO ?
CcSetAdditionalCacheAttributes();
CcFlushCache() … this routine is also used by the lazy writer for
write behind
The IO requested as the result of a flush is always paging IO. Implicitly
IO caused by lazy writer thread
will always be paging IO. Read ahead is peformed , as outlined earlier,
through page faults. You will see
nothing else for read ahead than paging IO requests.
- What are stream file objects ? Who uses them and for what?
Stream file objects are file objects created with one of the following 2
APIs: IoCreateStreamFileObject() and
IoCreateStreamFileObjectLite(). Stream file objects can be identified
through FO_STREAM_FILE flag in
FileObject->Flags. Stream file objects are only valid for paging IO. Note
that for stream file objects you
will never see a IRP_MJ_CREATE_OPERATION. For stream files objects created
with IoCreateStreamFileObject()
you will always see as the first operation a IRP_MJ_CLEANUP, followed by any
valid paging IO operations. For
stream file objects created with IoCreateStreamFileObjectLite(), a
IRP_MJ_CLEANUP is never seen. the first
operation performed on them can be any valid paging IO operation.
Stream file objects are ussualy used internally by file systems to map in
mmeory on disk data structures
for easy access (in system cache usually), and possibly to implement caching
of a file stream.
It is important for a filter driver to be prepared to handle those objects
correctly. Absentia of a
IRP_MJ_CREATE makes hard to corectly track state , and has a deep impact
over the reference counting
mechanism used. This hold true especially for the stream file objects
created with IoCreateStreamFileObjectLite()
for which not even a IRP_MJ_CLEANUP is seen by a filter driver.
- How should I track state ? What reference model should I use ?
Is up to you. Pay attention to stream file objects. A file system filter
driver should track state based on
contexts rather than file objects, whenever possible. Check last NT Insider
issue, it has a nice article about
reference counting. Your biggest concern here will be not to free your
tracking structures too early. For Windows XP+
only support and for file systems which support it , the logical choiche
is the new FsRtl context tracking mechanism. This mechanism works only with
active support from the underlieing
file system, and NOT all file systems are required to implement it. So even
if plan to use your driver in XP
only, the new context tracking mechanism might not work on 3rd party file
systems.
- Should I worry about FastIoRead , FastIoWrite && brothers ?
Generally speaking , data IO through fast io entry points will always be
satisfied from cache. Fast IO will
not be performed on stream files for which caching havent been initialized.
usually the Cc Copy interface is
used for FastIoRead / FastIoWrite. You will get to see the data written
through those interfaces as paging IO
coming from cache manager, in due time.
More general , the degree at which a filter driver of this kind have to
mess with FastIo path is highly
dependant of the design choosed.
- When is cache initialized on a file stream?
Since a file stream can be opened for other purposes than data IO , for
example for delete only, or
query attributtes, many file systems defer cache initialization for a file
stream untill they see the
first data IO operation incoming, beeit a IRP_MJ_READ or a IRP_MJ_WRITE.
Assuming in a filter driver, in post create processing, that cache was
initializaed for a file stream, even for a file created for buffered access,
s a mistake. You can check if the cache was intialized for a file stream by
using
FileObject->PrivateCacheMap
Dan