Is it right way to check the FO_CLEANUP_COMPLETE of file object to identify whether the cleanup for file is called or not?
Can we call FltGetStreamHandleContext() on such(FO_CLEANUP_COMPLETE is set) file objects in write callback?(write can occur after cleanup for memory mapped files)
It certainly works. Of course, the question has subtleties.
Just keep in mind that between cleanup and close the only operations on a file object (other than IRP_MJ_CLOSE) must have the IRP_PAGING_IO bit set. That’s because nobody other than the VM system (Mm, Cc) should be doing I/O to that file - cleanup represents teardown of the “user mode” state of the given file object. That means no cached I/O (for example).
With that said, some operations might work (non-cached read/write probably will work) due to vagaries of the implementation.
Absolutely. Until the file object is torn down (IRP_MJ_CLOSE) the context information is still present.
Note that you can’t obtain stream handle context information in post close.
In other
words, if there is a mapping and nothing gets written but some of the pages are
marked dirty you’ll still see paging writes.
What will be the case in which this will happen?
In our minifilter, we are also monitoring the file modification(including memory mapped files).
We have written a test application which executes following steps on sample file test.exe.
hFile = CreateFile()
hFileMapping = CreateFileMapping()
Close(hFile)------------------> Will result in cleanup
MapViewOfFile()
memcpy()
UnMapViewOfFile() ------------> Will result in write callback in minifilter
Close(hFileMapping)
If we execute 2(or more) instance of above application simultaneously, write callback corresponding to UnMapViewOfFile() gets called in system process context and
if we call FltGetStreamHandleContext(), it returns the same context in for both write callback.
Is it possible that the context returned in write callback will be same if files are memory mapped?
I’d be shocked if you didn’t. Remember, the stream handle context corresponds to the specific open instance (the “file object”) and since all paging I/O is being done against the same file object, it should return the same stream context.
So, for my test application with multiple instance running simultaneously, what I understand is:
hFile = CreateFile()------------------> File object will be different and hence stream handle context set in post create will be different.
hFileMapping = CreateFileMapping()----> If file is already mapped it will just point/reference to already backing file object.
Close(hFile)------------------> Will result in cleanup; Cleanup will be for different file objects and hence different stream handle context.
MapViewOfFile()
memcpy()
UnMapViewOfFile() ------------> Will result in write callback in minifilter; All write for memory mapped section will be on same file object and hence we will get same stream handle context.
Close(hFileMapping)
I have this diagram I use in my FS development class that demonstrates this point well. Basically it shows that there’s a (hidden) pointer to the file object from the specific section object. So all paging I/O activity is done against that file object.
In the most extreme case, you can actually find *three* different file objects referenced: one by the Data Section Object, one by the Image Section Object and then one by the Cache Manager.
Getting to this requires that you map the file (as data) before you do cached I/O to it:
(1) Open file X twice so you have H1 and H2 (which correspond to FO1 and FO2)
(2) Map the file via H1 - this will cause Mm to create a new section object (via DataSectionObject) and back it by FO1
(3) Read the file via H2 - this will cause the FSD to set up caching using FO2, so the shared cache map will point to FO2
At some point when the file is closed (before or after these other sequences) you then execute the file - this causes an open/map via the ImageSectionObject (backed by a third file object, FO3)
So DataSectionObject points to FO1
SharedCacheMap points to FO2
ImageSectionObject points to FO3