post close callback ambiguation

Hi all,

I have some temporary files generated by my minifilter all over the the operation time.
I want to delete this files when they are not needed anymore.

So i picked post close callback for this. as MSDN says:
“Receipt of this request(IRP_MJ_CLOSE) indicates that the last handle of the file object that is associated with the target device object has been closed and released. All outstanding I/O requests have been completed or canceled.”

My question is:

  1. Is post close the correct choice?

  2. where MSDN says “the last handle of the file object that is associated with the target device object has been closed and released”, dose that mean the fileObject most be released in post close??
    well, I am using the file object in post close and it is not released there. But I really expect it to be released.

  3. when I call ZwDeleteFile in post close sometimes I receive STATUS_SHARING_VIOLATION. I thought there should be no more open handle or file object to the file. why I am getting this error?

Maybe. It depends.
If I had such design I would schedule an asynchronous task from a system thread to avoid a recursive call to a file system on IRP_MJ_CLOSE path.

It has been already released by the IO Manager.
Do not touch it.
Do not call any IO on it.
Do not reference it.

Your assumption is wrong. The MSDN description is misleading. There still can be handles and outstanding IO that use another file object for the same data stream/file/device.

Anyway, I perplexed with a relation between a file object in IRP_MJ_CLOSE and a handle you use for ZwDeleteFile. Are they for the same data stream/file?

There is at least one opened handle or outstanding reference for the file and this handle or file object was not opened with FILE_SHARE_DELETE.

Remember that “last handle” means a handle and all its duplicates are now closed but other file objects representing the same file on disk may still be opened.

If you call CreateFile twice with the same path, you have two handles and two different file objects for the same file and you will get two IRP_MJ_CLOSE IRPs. But if you call CreateFile only once and then call DuplicateHandle, you have two handles, only one file object and you will get only one IRP_MJ_CLOSE when the last handle is closed or the last reference on the object is released.

H. G.

Thanks for the replies.

It has been already released by the IO Manager.
Do not touch it.
Do not call any IO on it.
Do not reference it.

It is weird!! The memory should be deleted by the time. Why I don’t get an exception? There is not even a verifier bugcheck.

Anyway, I perplexed with a relation between a file object in IRP_MJ_CLOSE and a
handle you use for ZwDeleteFile. Are they for the same data stream/file?

for
NTSTATUS ZwDeleteFile(
In POBJECT_ATTRIBUTES ObjectAttributes
);

I am not passing a handle. I obtain the file name using the released fileObject and initialize an object attributes structure.

Would you elaborate on file object and memory relation you are talking about?
Are you talking about a memory allocated for file object? The file object is still allocated but in post-close it has

  • reference count equals zero, that means you can’t call ObReferenceObject ( the reference is also zero in preoperation for a close request )
  • invalid FsContext, FsContext2, SectionObjectPointer pointers so any IO on it is not allowed

IRP_MJ_CLOSE is a request from the IO Manager before it releases memory allocated for a file object. In layman’s terms, it is like a destructor in C++. You don’t use C++ object for which a destructor has been called even if it still allocated from the pool/heap.

Internally the function issues IRP_MJ_CREATE, IRP_MJ_CLEANUP , IRP_MJ_CLOSE this is worse than just passing a handle for already opened file object. You are creating a dangerous recursion. This recursion is deadlock pron as all requests are issued from the top of the filters stack where some filters might hold exclusive locks to process IRP_MJ_CLOSE from which you are calling ZwDeleteFile.

>Would you elaborate on file object and memory relation you are talking about?

Are you talking about a memory allocated for file object?

Yes. I meant the memory allocated for the file object.

BTW, if I should not use the fileObject in IRP_MJ_CLOSE, then what useful information the callback parameters deliver. what is this used for callback when you can’t even know what is the path of the file which its fileObject is being released?

The irony is that FileObject->FileName is valid only in pre-create. After that a filter should not rely on its content. The RelatedFileObject makes the situation worse.

A filter should collect all information about file object when it observed IRP_MJ_CREATE and other requests and remove this information in IRP_MJ_CLOSE ( pre- or post operation, whatever ). A filter might query the Filter Manager for some information like file path but beware that the Filter Manager can not query file system in post-close.

From a file system point of view FileObject contains FsContext, FsContext2 and SectionObjectPointer that should be released in IRP_MJ_CLOSE. That is why they are invalid in post-close. You might find that FsContext and SectionObjectPointer are actually valid in post-close because they are shared with all file objects for the same data stream and removed when the last file object for a data stream is closed.

ZwDeleteFile is a PASSIVE_LEVEL only routine and cannot be called within a post operation callback. According to MSDN :

"Any I/O completion processing that needs to be performed at IRQL < DISPATCH_LEVEL cannot be performed directly in the postoperation callback routine. Instead, it must be posted to a work queue by calling a routine such as FltDoCompletionProcessingWhenSafe or FltQueueDeferredIoWorkItem.

Be aware that FltDoCompletionProcessingWhenSafe should never be called if the Flags parameter of the post-operation callback has the FLTFL_POST_OPERATION_DRAINING bit set. The following are exceptions to this rule:

1- If a minifilter driver’s pre-operation callback routine returns FLT_PREOP_SYNCHRONIZE for an IRP-based I/O operation, the corresponding post-operation callback routine is guaranteed to be called at IRQL <= APC_LEVEL, in the same thread context as the pre-operation callback.

2- Post-create callback routines are guaranteed to be called at IRQL PASSIVE_LEVEL, in the context of the thread that originated the IRP_MJ_CREATE operation. "

https://msdn.microsoft.com/en-us/library/windows/hardware/ff551107(v=vs.85).aspx

H. G.

IRQL issue increases the number of scenarios where this design fails.

I am quite sure that post-close is usually called at PASSIVE as IRP_MJ_CLOSE is issued at PASSIVE_LEVEL as the Object Manager schedules IRP_MJ_CLOSE to a kernel thread if IRQL> PASSIVE or all APCs are disabled. File system drivers normally call IoCompleteRequest for IRP_MJ_CLOSE without IRQL change or dispatching an Irp to a volume driver where it can be completed at elevated IRQL.

The problem here is mostly with a recursion and related deadlocks or data corruption. FltDoCompletionProcessingWhenSafe might not help here.

As usual MSDN is not precise and incomplete. Natural languages are too ambiguous. A good model must be defined in formal language. MSDN is a bad substitute for source code or mathematical model.

Thanks for the great pieces of information.
Thanks for the replies, Slava & H.G.

Is there any problem if I use the object pointer to obtain the streamHandle context I set in IRP_NJ_CREATE here in IRP_MJ_CLOSE?

I do not see any problem with this.