How to cancel a delete operation.

Hi,

I would like to cancel a delete operation in my minifilter. I’ve written the code that detects when a file is being deleted, but I’m unclear on how to actually cancel the operation. Can anyone help me out with this?

Here is my callback routine that detects for file deletion. Right now, all it does is send the file path to a user mode app.

FLT_POSTOP_CALLBACK_STATUS
PostProcessIrp (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in_opt PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
)
{

PFLT_FILE_NAME_INFORMATION nameInfo;
PFILE_DISPOSITION_INFORMATION fdi;
//BOOLEAN isDirectory;
FLT_FILE_NAME_OPTIONS nameOptions;
BOOLEAN scanFile;
NTSTATUS status;
PMDL *mdlAddress;
LOCK_OPERATION lockOp= IoReadAccess;
PVOID *buffer;
PULONG length;
UNICODE_STRING uniString;
FLT_PREOP_CALLBACK_STATUS cbStatus = FLT_POSTOP_FINISHED_PROCESSING;
UNREFERENCED_PARAMETER( CompletionContext );
UNREFERENCED_PARAMETER( Flags );
DbgPrint( “Scanner!PostProcessIrp Entered\n” );
if (!NT_SUCCESS( Data->IoStatus.Status ) || (STATUS_REPARSE == Data->IoStatus.Status))
{
return FLT_POSTOP_FINISHED_PROCESSING;
}

/* FltIsDirectory(Data->Iopb->TargetFileObject,FltObjects->Instance,&isDirectory);
if(isDirectory)
{
return FLT_POSTOP_FINISHED_PROCESSING;
}*/

switch(Data->Iopb->MajorFunction)
{
case IRP_MJ_SET_INFORMATION:
if (Data->Iopb->Parameters.SetFileInformation.FileInformationClass == FileDispositionInformation)
{
DbgPrint( “Scanner!PostProcessIrp FileDispositionInformation \n” );

status = FltDecodeParameters(Data, &mdlAddress, &buffer, &length, &lockOp);

DbgPrint( “Scanner!PostProcessIrp FileDispositionInformation buffer length: %d \n”, *length );

fdi = (PFILE_DISPOSITION_INFORMATION)(*buffer);

if (fdi->DeleteFile == TRUE)
{
status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo );

if (!NT_SUCCESS( status )) {

return FLT_POSTOP_FINISHED_PROCESSING;
}

FltParseFileNameInformation( nameInfo );

//
// Check if the extension matches the list of extensions we are interested in
//

if (RtlPrefixUnicodeString ( &TargetFolder, &nameInfo->ParentDir, TRUE ) == TRUE) {

scanFile = ScannerpCheckExtension( &nameInfo->Extension );

if (scanFile)
{
ScannerpSendMessageToUserApp ( &nameInfo->Name, 0, 1 );

}

}

FltReleaseFileNameInformation( nameInfo );
}
}

//TODO: Handle FileSetNameInformation for file renames.
break;
default:
;
}

return cbStatus;

}

Clear the delete on close flag, by a FltSetInformationFile.


Don Burn (MVP, Windows DKD)
Windows Filesystem and Driver Consulting
Website: http://www.windrvr.com
Blog: http://msmvps.com/blogs/WinDrvr

wrote in message news:xxxxx@ntfsd…
> Hi,
>
> I would like to cancel a delete operation in my minifilter. I’ve written
> the code that detects when a file is being deleted, but I’m unclear on how
> to actually cancel the operation. Can anyone help me out with this?
>
> Here is my callback routine that detects for file deletion. Right now,
> all it does is send the file path to a user mode app.
>
>
> FLT_POSTOP_CALLBACK_STATUS
> PostProcessIrp (
> inout PFLT_CALLBACK_DATA Data,
>
in PCFLT_RELATED_OBJECTS FltObjects,
> in_opt PVOID CompletionContext,
>
in FLT_POST_OPERATION_FLAGS Flags
> )
> {
>
> PFLT_FILE_NAME_INFORMATION nameInfo;
> PFILE_DISPOSITION_INFORMATION fdi;
> //BOOLEAN isDirectory;
> FLT_FILE_NAME_OPTIONS nameOptions;
> BOOLEAN scanFile;
> NTSTATUS status;
> PMDL mdlAddress;
> LOCK_OPERATION lockOp= IoReadAccess;
> PVOID buffer;
> PULONG length;
> UNICODE_STRING uniString;
> FLT_PREOP_CALLBACK_STATUS cbStatus = FLT_POSTOP_FINISHED_PROCESSING;
> UNREFERENCED_PARAMETER( CompletionContext );
> UNREFERENCED_PARAMETER( Flags );
> DbgPrint( “Scanner!PostProcessIrp Entered\n” );
> if (!NT_SUCCESS( Data->IoStatus.Status ) || (STATUS_REPARSE ==
> Data->IoStatus.Status))
> {
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> /

> FltIsDirectory(Data->Iopb->TargetFileObject,FltObjects->Instance,&isDirectory);
> if(isDirectory)
> {
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
/
>
> switch(Data->Iopb->MajorFunction)
> {
> case IRP_MJ_SET_INFORMATION:
> if (Data->Iopb->Parameters.SetFileInformation.FileInformationClass ==
> FileDispositionInformation)
> {
> DbgPrint( “Scanner!PostProcessIrp FileDispositionInformation \n” );
>
> status = FltDecodeParameters(Data, &mdlAddress, &buffer, &length,
> &lockOp);
>
> DbgPrint( “Scanner!PostProcessIrp FileDispositionInformation buffer
> length: %d \n”, *length );
>
> fdi = (PFILE_DISPOSITION_INFORMATION)(*buffer);
>
> if (fdi->DeleteFile == TRUE)
> {
> status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED |
> FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo );
>
> if (!NT_SUCCESS( status )) {
>
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> FltParseFileNameInformation( nameInfo );
>
> //
> // Check if the extension matches the list of extensions we are
> interested in
> //
>
>
> if (RtlPrefixUnicodeString ( &TargetFolder, &nameInfo->ParentDir, TRUE )
> == TRUE) {
>
> scanFile = ScannerpCheckExtension( &nameInfo->Extension );
>
> if (scanFile)
> {
> ScannerpSendMessageToUserApp ( &nameInfo->Name, 0, 1 );
>
>
> }
>
> }
>
> FltReleaseFileNameInformation( nameInfo );
> }
> }
>
> //TODO: Handle FileSetNameInformation for file renames.
> break;
> default:
> ;
> }
>
> return cbStatus;
>
> }
>
>
>
> Information from ESET NOD32 Antivirus, version of virus
> signature database 4738 (20100102)

>
> The message was checked by ESET NOD32 Antivirus.
>
> http://www.eset.com
>
>
>

Information from ESET NOD32 Antivirus, version of virus signature database 4738 (20100102)

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com

If I understand how a file is deleted, there are two types or IRP messages I can get: IRP_MJ_CREATE with the delete on close flag set and the IRP_MJ_SET_INFORMATION message. Is this solution appropriate for both messages?

Best Regards,
James

Yes, you can clear the bit no matter what.


Don Burn (MVP, Windows DKD)
Windows Filesystem and Driver Consulting
Website: http://www.windrvr.com
Blog: http://msmvps.com/blogs/WinDrvr

wrote in message news:xxxxx@ntfsd…
> If I understand how a file is deleted, there are two types or IRP messages
> I can get: IRP_MJ_CREATE with the delete on close flag set and the
> IRP_MJ_SET_INFORMATION message. Is this solution appropriate for both
> messages?
>
> Best Regards,
> James
>
>
> Information from ESET NOD32 Antivirus, version of virus
> signature database 4738 (20100102)

>
> The message was checked by ESET NOD32 Antivirus.
>
> http://www.eset.com
>
>
>

Information from ESET NOD32 Antivirus, version of virus signature database 4738 (20100102)

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com

Don’s experience with the disposition bit is different than mine - and
I’ve been frustrated by the varying semantics of these behaviors across
versions.

A FILE_DELETE_ON_CLOSE does not set the “delete pending” state of the
file. Thus, if I open the file twice, with one of them calling the
create with FILE_DELETE_ON_CLOSE option, the state of the disposition
will depend upon whether or not the file opened “for delete” has been
closed yet.

Here is the relevant bit from the FAT version of create.c (Win7 WDK):

//
// Mark the DeleteOnClose bit if the operation was
successful.
//

if ( DeleteOnClose ) {

SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
}

Note that this is set in the CCB.

From cleanup.c:

if (FlagOn(Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE)) {

ASSERT( NodeType(Fcb) != FAT_NTC_ROOT_DCB );

SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE);

Note that this promotes the bit in the CCB to the FCB.

Notice also that the CCB flag does not block opening the file
(create.c):

//
// If the longest prefix is pending delete (either the file or
// some higher level directory), we cannot continue.
//

if (FlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) {

try_return( Iosb.Status = STATUS_DELETE_PENDING );
}

Only the FCB bit is used.

If you query the bit, it uses the FCB (this is standard info, as from
the code it looks like query disposition isn’t implemented) - this is
FatQueryStandardInfo in fileinfo.c:

//
// Zero out the output buffer, and fill in the number of links
// and the delete pending flag.
//

RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );

Buffer->NumberOfLinks = 1;
Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState,
FCB_STATE_DELETE_ON_CLOSE );

When it comes to clearing the bit (fileinfo.c):

} else {

//
// The user doesn’t want to delete the file so clear
// the delete on close bit
//

DebugTrace(0, Dbg, “User want to not delete file\n”, 0);

ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
FileObject->DeletePending = FALSE;
}

Note that this clears the FCB flag, NOT the CCB flag.

This behavior differs between NTFS and FAT in some older versions (I
reported the difference as a bug in FAT - NTFS gave deterministic
behavior, FAT gave non-deterministic behavior. The developers at
Microsoft decided they preferred the non-deterministic FAT behavior.)
In current versions the behavior matches between FAT and NTFS.

Tony
OSR

Tony;

If I understand you correctly the FILE_DELETE_ON_CLOSE flag cannot be undone (as indicated in this link: http://www.osronline.com/showThread.cfm?link=67122).

Is that correct?

Thanks

In a sense. You cannot query or set the FILE_DELETE_ON_CLOSE flag (or rather the corresponding flag in the file systems) via normal APIs (once the CREATE happened) so you can’t “undo” it on that handle.

However, as Tony pointed out, when the handle is closed the flag will be promoted to an SCB flag which can be queried and set (and undone), provided that there is another handle.

Basically, if you have only one handle which was open with FILE_DELETE_ON_CLOSE (let’s call it H1), once it is closed the stream will be deleted. If you at least one more handle (H2), then once H1 is closed you can use H2 to query and reset the delete disposition which will then prevent the stream from being deleted.

Of course, any filter below yours could change the delete disposition (and they could even add FILE_DELETE_ON_CLOSE to CREATEs) so you need to think through the design quite carefully. Also, test with your minifilter attached multiple times (at different altitudes of course). It doesn’t have to work in this scenario (I imagine it’s not a supported scenario) but it shouldn’t bugcheck or corrupt data.

Regards,
Alex.
This posting is provided “AS IS” with no warranties, and confers no rights.

Rick,

I would not make a blanket statement of this type, actually.

I was trying to explain why “undoing” the delete in that case is a
surprisingly complicated operation to “get right”. But I believe it is
possible - just not simple by any means, and my concern was that my
observation about this issue was at odds with Don’s. Don may very well
be right and I’m missing something (it’s difficult when I’m doing this
from code reading.)

Alex’s subsequent point is a variation on something I’ve observed
numerous times: notably, you cannot know when a file is deleted from a
filter driver.

Tony
OSR

Don’t forget that not all deletes are deletes - it could just be an unlink.
This of course makes things particularly exciting in conjunction with Alex’s
observation about flags being promoted to the SCB.

Another pointer to remember is that a file can be deleted by a destructive
rename, and all you need to destroy the contents a SUPERSEDE of OVERWRITE
disposition (which, by the way may well delete any data streams).

you cannot know when a file is deleted from a filter driver.

I’d observe that the same is true of create. Just because you get
FILE_CREATED back in post create doesn’t mean that you haven’t already seen
a post-create (and possibly several other operations - including of course a
delete) for the file. If you combine all this with the requirement to not
hold locks during FSD operations it becomes obvious why filters which affect
the namespace can be quite challenging to write…

Rod

wrote in message news:xxxxx@ntfsd…
> If I understand how a file is deleted, there are two types or IRP messages
> I can get: IRP_MJ_CREATE with the delete on close flag set and the
> IRP_MJ_SET_INFORMATION message. Is this solution appropriate for both
> messages?
>
> Best Regards,
> James
>