Hi All,
I have a simple read-only FSD and I have problems with IRP_MJ_CLOSE of my FSD not being invoked for executable files (EXE).
If I start the executable, or simply select it in Windows Exporer, this triggers a few opens/closes on the file. During the last one when MJ_CLEANUP is invoked for the last handle to the executable, I flush the cash, purge the cache section and uninitialize the cache map.
Howerver, I see the following behaviour:
- after CcPurgeCacheSection() returns TRUE, the DataSectionObject pointer is not changed to 0 ( i.e. it is still valid ?! )
- my FSD gets one or two I/Os (MJ_READ) on the file object - I believe this is OK, at least the docss I’ve read say so.
- IRP_MJ_CLOSE for the fileobejct never gets invoked. Below I have described how I did manage to invoke the invocation, although it does not seem right.
On a subsequent open of the executable file - not starting it or viewing it in explorer, but as a data file, e.g. by typing in command prompt “type drive:path\my.exe” - a bizzarre (to my understanding) thing happens:
When the executable gets closed another MJ_CLEANUP is invoked for the new fileobejct. As the handle count of the FCB goes to 0 again, my routine flushes the cache and calls CcPurgeCacheSection().
This purge call invokes (apparently immediately, in the same thread, from within the MJ_CLEANUP of the new fileobject) MJ_CLOSE for the old/stalled fileobject!
After the MJ_CLEANUP is completed, MJ_CLOSE is also invoked for the new fileobject.
This only happens with EXEs, so I guess this must be due to some intricacy of the OS with EXEs.
Am I missing something obvious? Is there any way that I can “help” the OS with (or stop preventing it from) letting go of any references to the fileobject so that MJ_CLOSE is invoked in a timely fashion?
Many Thanks,
Theodor</drive:path>
Hi,
read in WDK about MmFlushImageSection and see examples of invoking MmFlushImageSection and CcPurgeCacheSection in CDFS example, pay attention on a sequence in which this functions are called.
Slava Imameyev , xxxxx@hotmail.com
Now the answers to some of your questions -
This purge call invokes … MJ_CLOSE
- CcPurgeCacheSection can call MmFlushImageSection, this depends on parameters, that is why you saw IRP_MJ_CLOSE for the file backing an image segment object.
after CcPurgeCacheSection() returns TRUE, the DataSectionObject pointer is not changed to 0
Yes, it is valid. You should also call CcUninitializeCacheMap if CcInitializeCacheMap was called. The system said to you - there is no any physical page with the file data but segment object is still valid.
- my FSD gets one or two I/Os (MJ_READ) on the file object
If after IRP_MJ_CLEANUP then this is OK, this means that this file objects is used for backing file mapping.
- IRP_MJ_CLOSE for the fileobejct never gets invoked
The following condition must be fulfilled for a file object backing a segment object can be “torn down” - there is no any valid mapping, i.e. no any process has a valid section object( data or executable section ) backed by this file. If this condition is hold then FSD and the system flushes and purges all physical pages associated with the segment objects( this object is shared by all section objects and there are up to two objects - data and executable ) and the system sends close request. The system caches executable an data segments if there is enough physical memory, so you should force purging by calling MmFlushImageSection and CcPurgeCacheSection.
Hi, thank you for your reply.
I realize I may have given more details on what I do in MJ_CLEANUP when the handle count drops to zero. In short:
CcFlushCache( &p_fcb->sect_obj_pointers, NULL, 0, NULL);
if( p_fcb->sect_obj_pointers.ImageSectionObject )
MmFlushImageSection( &p_fcb->sect_obj_pointers, MmFlushForWrite );
if( p_fcb->sect_obj_pointers.DataSectionObject )
CcPurgeCacheSection( &p_fcb->sect_obj_pointers, NULL, 0, FALSE);
CcUninitializeCacheMap( p_fobject, &p_fcb->nt_fcb.FileSize, NULL );
In my particular case ImageSectionObject is NULL so MmFlushImageSection doesn’t get called.
CcUnitializeCacheMap is always called on MJ_CLEANUP, regardless of the handle count.
I thought this should be enough to purge the executable from cahche/memory… apparently not.
If you have an idea why this would not be working, or better yet, how to make it work…
The issue here is that what you have isn’t mapped as an executable image (otherwise it would be using the image section) but a data file. Executables can switch between the two (think of “link and run” or “copy and run” for why this might happen.)
If you would like, you can write a program that will cause this to happen for data files as well - open the file, map it, close the file and then keep the mapping around. The purge is going to fail. But your code doesn’t check for purge failing - something you might want to consider doing to gain further insight.
Tony
Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com
Thanks, Tony.
I do check the result of CcPurgeCacheSection ( I have omitted some code for simplicity ) - the purge returns TRUE - I’m not sure if you mean other type of failure and how it can be checked.
The CcUninitializeCacheMap(), on the other hand, always returns FALSE, even for objects for which MJ_CLOSE is invoked “immediately” after MJ_CLEANUP.
What puzzles me is that a later call to CcPurge would directly invoke the MJ_CLOSE - while the first successful (returning TRUE) call should have been enough…
Although it may not be very elegant, I could design the driver to call CcPurge at a later time - the problem is that I don’t know when - clearly the return value of CcPurge does not imply if MJ_CLOSE is going to be (or has been) invoked.
Is there any other way of determining the state of the fileobject?
Would using MmForceSectionClosed() be of any good at a particular stage of the life cycle of the fileobject?
I agree with the observetion that the file is mapped as a data file and not as an executable - but I only get this behaviour executables (it also happens if I select a DLL in Windows Explorer).
One more bit of information.
For the executables I get MJ_READ after the MJ_CLEANUP, while for the other objects I get MJ_CLOSE.
The MJ_READ have IRP_PAGING_IO, IRP_NOCACHE and FO_SYNCHRONOUS_IO (I am not sure this is relevant).
Is it that I need to do something special in the MJ_READs coming after MJ_CLEANUP so that MJ_CLOSE is invoked, without the need of a subsequent CcPurge…
This is just a wild guess - I am so confused.
>CcUninitializeCacheMap(), on the other hand, always returns FALSE, even for objects for which MJ_CLOSE is invoked “immediately” after MJ_CLEANUP.
This might mean that there is another file object which uses the shared cache map. Call CcPurgeCacheSection with the lats parameter( UninitializeCacheMaps ) set to TRUE - this will help to close private cache maps and dereferences shared cache map, but if the file backs the segment object and there is a valid section object the IRP_MJ_CLOSE won’t arrive until the section closed.
The MJ_READ have IRP_PAGING_IO, IRP_NOCACHE and FO_SYNCHRONOUS_IO
Request from the page fault handler. Only the page fault handler can generate read requests on a file if IRP_MJ_CLOSE has been sent.
Is it that I need to do something special in the MJ_READs coming after MJ_CLEANUP
No.
Slava Imameyev , xxxxx@hotmail.com
Opps, I must correct myself -
“Request from the page fault handler. Only the page fault handler can generate read requests on a file if IRP_MJ_CLOSE has been sent.”
should be read as
“Request from the page fault handler. Only the page fault handler can generate read requests on a file if IRP_MJ_CLEANUP has been sent.”
Slava Imameyev, xxxxx@hotmail.com