Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTFSD

Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


How to cancel a delete operation.

OSR_Community_UserOSR_Community_User Member Posts: 110,217
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;

}

Comments

  • Don_Burn_1Don_Burn_1 Member Posts: 4,311
    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
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    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
  • Don_Burn_1Don_Burn_1 Member Posts: 4,311
    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
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    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
  • Rick_WRick_W Member Posts: 126
    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
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    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.
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    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
  • rod_widdowsonrod_widdowson Member - All Emails Posts: 1,266
    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

    <[email protected]> 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
    >
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. Sign in or register to get started.

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Kernel Debugging 9-13 Sept 2024 Live, Online
Developing Minifilters 15-19 July 2024 Live, Online
Internals & Software Drivers 11-15 Mar 2024 Live, Online
Writing WDF Drivers 20-24 May 2024 Live, Online