Hi!
I work on transparent file encryption (minifilter-based). A lot of time and effort was spend...
At now, I am struggling with one critical architectural problem and, in my opinion, the topic has been never discussed anywhere at all.
Let's assume that you need to maintain some 'state' in your driver and keep it consistent with underlying file system.
This state, for example, is a set of file names, their sizes, attributes, some metadata and other characteristics.
When something like rename occurs in file system, you have to reflect these changes in your state - update/replace some paths,
write new values to attributes, metadata. When a file is being grown or truncated, you have to save it's new EOF.
Writes may move VDL forward. And so on... A lot of other I/O have to be handled in a similar way. The state may be
volatile or persistent (e.g. stored in-memory only or on-disk too).
Thanks for reading this far, we have come to the main point
The problem is that minifilter may see completion of requests 'out of order'. In other words, an order in which file system
completes I/O requests and an order in which these completions are observed by minifilter, may differ. By obvious reasons:
context switches, task/priority scheduling, presence of other filters below you that like to defer some operations to
separate thread, etc.
Let me give you several real-world examples (they are not related exclusively to encryption minifilters; some sort of
these problems may appear in other software types).
Let's say we have three files: X, Y and Z. And two parallel threads: thread-A and thread-B. Thread-A tries to rename X to Y,
thread-B tries to rename Y to Z in the same time (both with replacing original files).
thread-A: X-Y
thread-B: Y-Z
If thread-A "wins" (in the point of view of the file system!), only file Z will remain (Z is former Y that is former X).
If thread-B "wins", there will be two files: Z (former Y) and Y (former X).
But! A minifilter may observe these events in arbitrary order and this will made its state inconsistent.
Two or more threads simultaneously call IRP_MJ_SET_INFORMATION/FileDispositionInformaion(Ex). Some threads intent to
delete file, some other cancel deleting (by setting or removing the corresponding member of 'FILE_DISPOSITION_INFORMATION').
These calls will by synchronized somewhere inside file system, but in minifilter we may observe any order of completions.
We have no simple ways to track deletion! Yes, there is great example in WDK samples ("delete" minifilter), but the
proposed approach works only with NTFS/ReFS.
A race between truncating a file and writing into this file (as a result of which it will become unclear where is EOF and VDL now).
Two or more threads simultaneously append data to the end of file. Look, for example to the FILE_WRITE_TO_END_OF_FILE flag.
By the way, i didn't find any discussions about this flag. It feels like everything is working for everyone. And this is little annoying for me
Recently I found partial solution - serialize some I/O requests on a stream level (or even on a volume level). For example, when all
rename requests will be serialized on a per-volume basis, the name consistency problems will gone. The races with file deletion
may be solved by similar way (if you serialize corresponding IRP_MJ_SET_INFORMATION and IRP_MJ_CLEANUP at least on a stream level).
But it's not possible to serialize paging I/O, for example... So, some part of events will always be observed 'out of sync'.
I attempted to use counters, per-handle VDL views, etc. But without success.
So, guys, thanks to everyone for reading so far.
How to write encryption minifilter properly, in a correct way? Is that way does exist? I have doubts about that...
Just for information: this is not my first minifilter or first kernel-mode driver; I have nearly 15 years of experience in this.
How to deal with races described above?
What alternative design model should be considered for such project? Isolation filter? Redirector? Virtual volume? Layered FSD? Shadow File Objects?..
I am looking for any ideas, suggestions, and just thoughts (any of these will be highly appreciated).
Thanks!