MetadataManager


/*++

Copyright (c) 1999 - 2002  Microsoft Corporation

Module Name:

    pch.h

Abstract:

    This module includes all  the headers which need to be
    precompiled & are included by all the source files in this
    project


Environment:

    Kernel mode


--*/

#ifndef __FMM_PCH_H__
#define __FMM_PCH_H__

//
//  Enabled warnings
//

#pragma warning(error:4100)     //  Enable-Unreferenced formal parameter
#pragma warning(error:4101)     //  Enable-Unreferenced local variable
#pragma warning(error:4061)     //  Eenable-missing enumeration in switch statement
#pragma warning(error:4505)     //  Enable-identify dead functions

//
//  Includes
//

#include 
  
  
   
   
#include 
   
   
    
    
#include 
    
    
     
     
#include "MetadataManagerStruc.h"
#include "MetadataManagerProc.h"


#endif __FMM_PCH_H__

/*++

Copyright (c) 1999 - 2002  Microsoft Corporation

Module Name:

    MetadataManagerStruct.h

Abstract:

    This is the header file defining the data structures used by the kernel mode
    filter driver implementing filter metadata manager.


Environment:

    Kernel mode


--*/

//
//  If this is 1, then the filter will validate that the metadata file is indeed open 
//  whenever a create suceeds on the volume
//

#define VERIFY_METADATA_OPENED 0


//
//  Memory Pool Tags
//

#define FMM_STRING_TAG                        'tSmF'
#define FMM_INSTANCE_CONTEXT_TAG              'cImF'


//
//  Filter metadata management filter global data
//

typedef struct _FMM_GLOBAL_DATA {

    //
    // Handle to minifilter returned from FltRegisterFilter()
    //

    PFLT_FILTER Filter;


#if DBG

    //
    // Field to control nature of debug output
    //

    ULONG DebugLevel;
#endif

} FMM_GLOBAL_DATA, *PFMM_GLOBAL_DATA;

extern FMM_GLOBAL_DATA Globals;




//
//  Instance context flags and data structure
//

//
//  Indicates that the instance context resource has been released 
//  before performing a file system operation that could potentially
//  cause the resource to be re-acquired and deadlock the system
//

#define INSTANCE_CONTEXT_F_TRANSITION           0x00000001


//
//  Indicates if the filter has opened the metadata file and
//  holds a reference to the metadata file object for the
//  volume
//

#define INSTANCE_CONTEXT_F_METADATA_OPENED      0x00000002


typedef struct _FMM_INSTANCE_CONTEXT {

    //
    //  Flags for this instance - defined as INSTANCE_CONTEXT_F_XXX
    //

    ULONG Flags;

    //
    //  Instance for this context.
    //

    PFLT_INSTANCE Instance;

    //
    //  File System Type for this instance.
    //

    FLT_FILESYSTEM_TYPE FilesystemType;

    //
    //  Volume associated with this instance.
    //

    PFLT_VOLUME Volume;

    //
    //  Resource for synchronizing  access to the metadata file. 
    //  This recource may also be overloaded to control access to in-memory
    //  structures that hang off the instance context of the volume.
    //

    ERESOURCE MetadataResouce;

    //
    //  Handle of the metadata file.
    //

    HANDLE MetadataHandle;

    //
    //  File object of the metadata file.
    //

    PFILE_OBJECT MetadataFileObject;

    //
    //  The file object on cleanup or cancel removal of which we need to re-open
    //  our metadata file. This is basically the file object on which we received
    //  an explicit or implicit lock or a pnp query removal that caused us to
    //  drop the references to our metadata file
    //

    PFILE_OBJECT MetadataOpenTriggerFileObject;

} FMM_INSTANCE_CONTEXT, *PFMM_INSTANCE_CONTEXT;

#define FMM_INSTANCE_CONTEXT_SIZE         sizeof( FMM_INSTANCE_CONTEXT )


//
//  Name of the metadata file for this filter.
//  In this sample, we put the metadata file in the SystemVolumeInformation
//  folder so as to demonstrate creation of this folder if it does not
//  exist
//

#define FMM_METADATA_FILE_NAME                L"\\System Volume Information\\FilterMetadata.md"
#define FMM_METADATA_FILE_NAME_LENGTH         (sizeof( FMM_METADATA_FILE_NAME ) - sizeof( WCHAR ))

//
//  Default length of the volume name.
//

#define FMM_DEFAULT_VOLUME_NAME_LENGTH        64


//
//  Debug helper functions
//

#if DBG


#define DEBUG_TRACE_ERROR                           0x00000001  // Errors - whenever we return a failure code
#define DEBUG_TRACE_LOAD_UNLOAD                     0x00000002  // Loading/unloading of the filter
#define DEBUG_TRACE_INSTANCES                       0x00000004  // Attach / detatch of instances

#define DEBUG_TRACE_METADATA_OPERATIONS             0x00000008  // Operation to access / modify in memory metadata

#define DEBUG_TRACE_ALL_IO                          0x00000010  // All IO operations tracked by this filter

#define DEBUG_TRACE_INFO                            0x00000020  // Misc. information

#define DEBUG_TRACE_ALL                             0xFFFFFFFF  // All flags


#define DebugTrace(Level, Data)                     \
    if ((Level) & Globals.DebugLevel) {               \
        DbgPrint Data;                              \
    }


#else

#define DebugTrace(Level, Data)             {NOTHING;}

#endif

/*++

Copyright (c) 1999 - 2002  Microsoft Corporation

Module Name:

    MetadataManagerProc.h

Abstract:

    This is the header file defining the functions of the kernel mode
    filter driver implementing filter metadata management.


Environment:

    Kernel mode


--*/

#define MAKE_RESOURCE_OWNER(X) (((ERESOURCE_THREAD)(X)) | 0x3)

//
//  Functions implemented in operations.c
//

FLT_PREOP_CALLBACK_STATUS
FmmPreCreate (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );

FLT_POSTOP_CALLBACK_STATUS
FmmPostCreate (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in PVOID CbdContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    );

FLT_PREOP_CALLBACK_STATUS
FmmPreCleanup (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );

FLT_POSTOP_CALLBACK_STATUS
FmmPostCleanup (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __inout PVOID CompletionContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    );


FLT_PREOP_CALLBACK_STATUS
FmmPreFSControl (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );

FLT_POSTOP_CALLBACK_STATUS
FmmPostFSControl (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in PVOID CompletionContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    );

FLT_PREOP_CALLBACK_STATUS
FmmPreDeviceControl (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );

FLT_POSTOP_CALLBACK_STATUS
FmmPostDeviceControl (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in PVOID CbdContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    );

FLT_PREOP_CALLBACK_STATUS
FmmPreShutdown (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );

FLT_PREOP_CALLBACK_STATUS
FmmPrePnp (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    );

FLT_POSTOP_CALLBACK_STATUS
FmmPostPnp (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in PVOID CbdContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    );

//
//  Functions implemented in datastore.c
//

__drv_mustHoldCriticalRegion
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
FmmOpenMetadata (
    __in PFMM_INSTANCE_CONTEXT InstanceContext,
    __in BOOLEAN CreateIfNotPresent
    );

__drv_mustHoldCriticalRegion
__drv_maxIRQL(PASSIVE_LEVEL)
VOID
FmmCloseMetadata (
    __in PFMM_INSTANCE_CONTEXT InstanceContext
    );

NTSTATUS
FmmReleaseMetadataFileReferences (
    __inout PFLT_CALLBACK_DATA Cbd
    );

NTSTATUS
FmmReacquireMetadataFileReferences (
    __inout PFLT_CALLBACK_DATA Cbd
    );

NTSTATUS
FmmSetMetadataOpenTriggerFileObject (
    __inout PFLT_CALLBACK_DATA Cbd
    );

__drv_mustHoldCriticalRegion
__drv_releasesCriticalRegion
__drv_maxIRQL(APC_LEVEL)
VOID
FmmBeginFileSystemOperation (
    IN PFMM_INSTANCE_CONTEXT InstanceContext
    );

__drv_neverHoldCriticalRegion
__drv_acquiresCriticalRegion
__drv_maxIRQL(APC_LEVEL)
VOID
FmmEndFileSystemOperation (
    IN PFMM_INSTANCE_CONTEXT InstanceContext
    );


#if VERIFY_METADATA_OPENED
    
NTSTATUS
FmmIsMetadataOpen (
    __inout PFLT_CALLBACK_DATA Cbd,
    __out BOOLEAN* MetadataOpen
    );

#endif

//
//  Functions implemented in support.c
//

NTSTATUS
FmmAllocateUnicodeString (
    __inout PUNICODE_STRING String
    );

VOID
FmmFreeUnicodeString (
    __inout PUNICODE_STRING String
    );

BOOLEAN
FmmTargetIsVolumeOpen (
    __in PFLT_CALLBACK_DATA Cbd
    );

NTSTATUS
FmmIsImplicitVolumeLock( 
    __in PFLT_CALLBACK_DATA Cbd,
    __out PBOOLEAN IsLock
    );

//
//  Lock primitives
//

__drv_neverHoldCriticalRegion
__drv_acquiresCriticalRegion
__drv_maxIRQL(APC_LEVEL)
FORCEINLINE
VOID
FmmAcquireResourceExclusive (
    IN PERESOURCE Resource
    )
{
    ASSERT(KeGetCurrentIrql() <= apc_level="" assert="" keentercriticalregion="" resource="" true="" __drv_neverholdcriticalregion="" __drv_acquirescriticalregion="" __drv_maxirql="" forceinline="" void="" fmmacquireresourceshared="" in="" peresource="" resource="" assert="" apc_level="" keentercriticalregion="" resource="" true="" __drv_mustholdcriticalregion="" __drv_releasescriticalregion="" __drv_maxirql="" forceinline="" void="" fmmreleaseresource="" in="" peresource="" resource="" assert="" dispatch_level="" assert="" exisresourceacquiredsharedlite="" exreleaseresourcelite="" keleavecriticalregion="" copyright="" 2002="" -="" 2003="" microsoft="" corporation="" module="" name:="" datastore="" abstract:="" this="" module="" contains="" routines="" that="" provide="" support="" for="" storage="" and="" retrieval="" of="" the="" filter="" metadata="" manager="" filter="" metadata="" environment:="" kernel="" mode="" --="" assign="" text="" sections="" for="" each="" routine="" alloc_pragma="" alloc_text="" fmmopenmetadata="" alloc_text="" fmmclosemetadata="" alloc_text="" fmmreleasemetadatafilereferences="" alloc_text="" fmmreacquiremetadatafilereferences="" alloc_text="" fmmsetmetadataopentriggerfileobject="" alloc_text="" fmmbeginfilesystemoperation="" alloc_text="" fmmendfilesystemoperation="" __drv_mustholdcriticalregion="" __drv_maxirql="" ntstatus="" fmmopenmetadata="" __in="" pfmm_instance_context="" instancecontext="" __in="" boolean="" createifnotpresent="" routine="" description:="" this="" routine="" opens="" or="" creates="" the="" fmm="" metadata="" on="" the="" specified="" instance="" arguments:="" instancecontext="" -="" supplies="" the="" instance="" context="" for="" this="" instance="" createifnotpresent="" -="" supplies="" if="" the="" directory="" entry="" must="" be="" created="" if="" it="" is="" not="" present="" return="" value:="" returns="" the="" status="" of="" this="" operation="" note:="" the="" caller="" must="" hold="" the="" instance="" context="" resource="" exclusive="" when="" this="" routine="" is="" called="" --="" object_attributes="" objectattributes="" io_status_block="" iostatus="" unicode_string="" filename="" ntstatus="" status="" ulong="" length="" paged_code="" debugtrace="" debug_trace_metadata_operations="" opening="" metadata="" file="" createifnotpresent="%X)\n'," instancecontext-="">Volume,
                 CreateIfNotPresent) );

    status = STATUS_SUCCESS;
    fileName.Buffer = NULL;

    //
    //  Get the volume name and construct the full metadata filename.
    //


    length = FMM_DEFAULT_VOLUME_NAME_LENGTH + FMM_METADATA_FILE_NAME_LENGTH;

#pragma warning(push)
#pragma warning(disable:4127) //  Conditional expression is constant
    while (TRUE) {

#pragma warning(pop)

        fileName.MaximumLength = (USHORT)length;

        status = FmmAllocateUnicodeString( &fileName );

        if (!NT_SUCCESS( status )) {

            goto FmmOpenMetadataCleanup;
        }

        status = FltGetVolumeName( InstanceContext->Volume, &fileName, &length );

        if (NT_SUCCESS( status )) {

            status = RtlAppendUnicodeToString( &fileName, FMM_METADATA_FILE_NAME );

            if (NT_SUCCESS( status )) {

                break;
            }
        } else {

            DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR,
                        ("[Fmm]: Failed to get volume name (Volume = %p, Status = 0x%x)\n",
                         InstanceContext->Volume,
                         status) );
        }


        if (status != STATUS_BUFFER_TOO_SMALL) {

            goto FmmOpenMetadataCleanup;;
        }

        //
        //  Free the filename buffer since a bigger one will be allocated
        //  above
        //

        FmmFreeUnicodeString( &fileName );

        length += FMM_METADATA_FILE_NAME_LENGTH;
    }


    //
    //  Initialize the object attributes and open the file.
    //

    InitializeObjectAttributes( &objectAttributes,
                                &fileName,
                                OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL );




RetryFltCreateFile:


    DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                ("[Fmm]: Calling FltCreateFile for metadata file %wZ (Volume = %p, Status = 0x%x)\n",
                 &fileName,
                 InstanceContext->Volume,
                 status) );


    //
    //  Mark the beginning of a file system operation
    //

    FmmBeginFileSystemOperation( InstanceContext );

    status = FltCreateFile( Globals.Filter,
                            InstanceContext->Instance,
                            &InstanceContext->MetadataHandle,
                            FILE_ALL_ACCESS,
                            &objectAttributes,
                            &ioStatus,
                            (PLARGE_INTEGER) NULL,
                            FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
                            FILE_SHARE_READ,
                            (CreateIfNotPresent ? FILE_OPEN_IF : FILE_OPEN),
                            0L,
                            NULL,
                            0L,
                            0 );

    //
    //  Mark the end of a file system operation
    //

    FmmEndFileSystemOperation( InstanceContext );


    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR,
                    ("[Fmm]: FltCreateFile failure for metadata file %wZ (Volume = %p, Status = 0x%x)\n",
                     &fileName,
                     InstanceContext->Volume,
                     status) );

        if (CreateIfNotPresent && (status == STATUS_OBJECT_PATH_NOT_FOUND)) {

            //
            //  We need to create the metadata file and the creation failed
            //  because the SystemVolumeInformation folder does not exist.
            //  So, create the folder and try again.
            //

            DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                        ("[Fmm]: Creating SystemVolumeInformation folder for metadata file %wZ (Volume = %p, Status = 0x%x)\n",
                         &fileName,
                         InstanceContext->Volume,
                         status) );


            //
            //  Mark the beginning of a file system operation
            //

            FmmBeginFileSystemOperation( InstanceContext );

            status = FltCreateSystemVolumeInformationFolder( InstanceContext->Instance );

            //
            //  Mark the end of a file system operation
            //

            FmmEndFileSystemOperation( InstanceContext );



            if (NT_SUCCESS( status )) {

                //
                //  We have sucessfully created the SystemVolumeInformation folder
                //  Try to create the metadata file again
                //

                goto RetryFltCreateFile;
            } else {

                DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR,
                            ("[Fmm]: FltCreateSystemVolumeInformationFolder failure for metadata file %wZ (Volume = %p, Status = 0x%x)\n",
                             &fileName,
                             InstanceContext->Volume,
                             status) );
            }
        }

        goto FmmOpenMetadataCleanup;
    }

    //
    //  Retrieve the FileObject from the handle created
    //

    status = ObReferenceObjectByHandle( InstanceContext->MetadataHandle,
                                        STANDARD_RIGHTS_REQUIRED,
                                        *IoFileObjectType,
                                        KernelMode,
                                        &InstanceContext->MetadataFileObject,
                                        NULL );
    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR,
                    ("[Fmm]: Failure to get file object from handle for metadata file %wZ (Volume = %p, Status = 0x%x)\n",
                     &fileName,
                     InstanceContext->Volume,
                     status) );

        goto FmmOpenMetadataCleanup;
    }

    if (ioStatus.Information == FILE_CREATED) {

        //
        //  New metadata was created
        //

        DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: Created new metadata file %wZ (Volume = %p, Status = 0x%x)\n",
                     &fileName,
                     InstanceContext->Volume,
                     status) );

        //
        //  The filter may want to do some initialization on the newly created
        //  metadata file here like adding a header to the file
        //

    }
    else {

        //
        //  Existing metadata was opened
        //

        DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: Opened existing metadata file %wZ (Volume = %p, Status = 0x%x)\n",
                     &fileName,
                     InstanceContext->Volume,
                     status) );

        //
        //  The filter may want to do some sanity checks on the metadata file here
        //  like validating the header of the file
        //

    }

    //
    //  Here the filter may read the metadata contents and initialize
    //  its in memory data structures with the data from the metadata
    //  file
    //


FmmOpenMetadataCleanup:

    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS | DEBUG_TRACE_ERROR,
                    ("[Fmm]: Failed to open metadata (Volume = %p, Status = 0x%x)\n",
                     InstanceContext->Volume,
                     status) );

        //
        //  CLose the handle and dereference the file object
        //

        if (InstanceContext->MetadataHandle) {


            //
            //  Mark the beginning of a file system operation
            //

            FmmBeginFileSystemOperation( InstanceContext );

            FltClose( InstanceContext->MetadataHandle );

            //
            //  Mark the end of a file system operation
            //

            FmmEndFileSystemOperation( InstanceContext );

            InstanceContext->MetadataHandle = NULL;

            if (InstanceContext->MetadataFileObject) {

                ObDereferenceObject( InstanceContext->MetadataFileObject );
                InstanceContext->MetadataFileObject = NULL;
            }
        }
    } else {

        DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: Metadata successfully opened (Volume = %p)\n",
                     InstanceContext->Volume) );

        //
        //  Set flags to indicate successful open of filter metadata
        //

        SetFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED );

    }

    if (fileName.Buffer != NULL) {

        FmmFreeUnicodeString( &fileName );
    }

    return status;
}


__drv_mustHoldCriticalRegion
__drv_maxIRQL(PASSIVE_LEVEL)
VOID
FmmCloseMetadata (
    __in PFMM_INSTANCE_CONTEXT InstanceContext
    )
/*++

Routine Description:

    This routine closes the filters handle to the metadata file.

Arguments:

    InstanceContext - Instance context for this instance.

Return Value:

    Void.

Note:

    The caller must hold the instance context resource when this routine is called.


--*/
{
    PAGED_CODE();

    ASSERT( InstanceContext->MetadataHandle );
    ASSERT( InstanceContext->MetadataFileObject );

    DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                ("[Fmm]: Closing metadata file ... (Volume = %p)\n",
                 InstanceContext->Volume ) );

    //
    //  Dereference the file object and close the file handle.
    //

    ObDereferenceObject( InstanceContext->MetadataFileObject );

    InstanceContext->MetadataFileObject = NULL;


    //
    //  Mark the beginning of a file system operation
    //

    FmmBeginFileSystemOperation( InstanceContext );

    FltClose( InstanceContext->MetadataHandle );

    //
    //  Mark the end of a file system operation
    //

    FmmEndFileSystemOperation( InstanceContext );


    InstanceContext->MetadataHandle = NULL;

    //
    //  Reset flag to indicate filter metadata is closed
    //

    ClearFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED );

}

NTSTATUS
FmmReleaseMetadataFileReferences (
    __inout PFLT_CALLBACK_DATA Cbd
    )
/*++

Routine Description:

    This routine releases all references to the metadata file on the specified instance.

Arguments:

    Cbd                 - Supplies a pointer to the callbackData which
                          declares the requested operation.

Return Value:

    Status

Note:

    This routine takes care of the synchronization needed to access the metadata
    file object and handle

    This routine will also set the MetadataOpenTriggerFileObject in the instance context
    to the file object of the volume that triggered the release of the metadata file
    references.

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PFMM_INSTANCE_CONTEXT instanceContext = NULL;

    PAGED_CODE();

    //
    //  Get the instance context
    //

    status = FltGetInstanceContext( Cbd->Iopb->TargetInstance,
                                    &instanceContext );
    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmReleaseMetadataFileReferences -> Failed to get instance context.\n") );

        goto FmmReleaseMetadataFileReferencesCleanup;
    }

    //
    //  Acquire exclusive access to the instance context
    //

    FmmAcquireResourceExclusive( &instanceContext->MetadataResouce);

    if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION)) {

        //
        //  If this instance context is in a transition state, it implies that
        //  the instance context lock has been released while sending an operation
        //  down to the file system. The reason for doing so is to prevent a potential
        //  deadlock if an underlying filter sends an IO to the top of the filter
        //  stack while we are holding the resource
        //
        //  We have managed to acquire this resource in this state of transition.
        //  It would be incorrect to use or modify the instance context in any way
        //  in this situation. So we simply let go.
        //

        status = STATUS_FILE_LOCK_CONFLICT;

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmReleaseMetadataFileReferences -> Failed to get exclusive access to instance context since it is in a state of transition.\n") );
    } else {

        //
        //  Close the metadata file if it is open
        //

        if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) {

            DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                        ("[Fmm]: FmmReleaseMetadataFileReferences -> Releasing references to metadata handle and file object (InstanceContext = %p VolumeFileObject = %p)\n",
                         instanceContext,
                         Cbd->Iopb->TargetFileObject) );

            //
            //  Close the metadata file object
            //

            FmmCloseMetadata( instanceContext );

            //
            //  Save the volume file object for which we are releasing our references
            //

            instanceContext->MetadataOpenTriggerFileObject = Cbd->Iopb->TargetFileObject;
        } else {

            DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                        ("[Fmm]: FmmReleaseMetadataFileReferences -> Exit without attempting to release references to metadata handle and file object (InstanceContext = %p, VolumeFileObject = %p, MetadataOpenTriggerFileObject = %p, MetadataAlreadyOpen = 0x%x)\n",
                         instanceContext,
                         Cbd->Iopb->TargetFileObject,
                         instanceContext->MetadataOpenTriggerFileObject,
                         FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) );
        }
    }

    //
    //  Relinquish exclusive access to the instance context
    //

    FmmReleaseResource( &instanceContext->MetadataResouce);


FmmReleaseMetadataFileReferencesCleanup:

    //
    // Release the references we have acquired
    //

    if (instanceContext != NULL) {

        FltReleaseContext( instanceContext );
    }


    return status;
}


NTSTATUS
FmmReacquireMetadataFileReferences (
    __inout PFLT_CALLBACK_DATA Cbd
    )
/*++

Routine Description:

    This routine re-acquires references to the metadata file on the specified instance.

Arguments:

    Cbd                 - Supplies a pointer to the callbackData which
                          declares the requested operation.

Return Value:

    Status

Note:

    This routine takes care of the synchronization needed to access the metadata
    file object and handle


    This routine will also NULL the MetadataOpenTriggerFileObject in the instance context
    if it was successfully able to open the metadata file references.


--*/
{

    NTSTATUS status = STATUS_SUCCESS;
    PFMM_INSTANCE_CONTEXT instanceContext = NULL;

    PAGED_CODE();

    //
    //  Get the instance context
    //

    status = FltGetInstanceContext( Cbd->Iopb->TargetInstance,
                                    &instanceContext );
    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmReacquireMetadataFileReferences -> Failed to get instance context.\n") );

        goto FmmReacquireMetadataFileReferencesCleanup;
    }

    //
    //  Acquire exclusive access to the instance context
    //

    FmmAcquireResourceExclusive( &instanceContext->MetadataResouce );

    if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION)) {

        //
        //  If this instance context is in a transition state, it implies that
        //  the instance context lock has been released while sending an operation
        //  down to the file system. The reason for doing so is to prevent a potential
        //  deadlock if an underlying filter sends an IO to the top of the filter
        //  stack while we are holding the resource
        //
        //  We have managed to acquire this resource in this state of transition.
        //  It would be incorrect to use or modify the instance context in any way
        //  in this situation. So we simply let go.
        //

        status = STATUS_FILE_LOCK_CONFLICT;

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmReacquireMetadataFileReferences -> Failed to get exclusive access to instance context since it is in a state of transition.\n") );
    } else {

        //
        //  Re-open the metadata only if the trigger file object match the file object that
        //  caused this function to be called
        //

        if (instanceContext->MetadataOpenTriggerFileObject == Cbd->Iopb->TargetFileObject) {

            //
            //  Open the filter metadata file (do not read the file since we already have
            //  stuff in memory and do not create if the file does not exist
            //

            if (!FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) {

                DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                            ("[Fmm]: FmmReacquireMetadataFileReferences -> Re-acquiring references to metadata handle and file object (InstanceContext = %p, VolumeFileObject = %p)\n",
                             instanceContext,
                             Cbd->Iopb->TargetFileObject) );

                status = FmmOpenMetadata( instanceContext,
                                                 FALSE );

                //
                //  Reset the trigger file object since the volume open failed.
                //

                instanceContext->MetadataOpenTriggerFileObject = NULL;

            } else {

                DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                            ("[Fmm]: FmmReacquireMetadataFileReferences -> Exit without attempting to re-acquire references to metadata handle and file object (InstanceContext = %p, VolumeFileObject = %p, MetadataOpenTriggerFileObject = %p, MetadataAlreadyOpen = 0x%x)\n",
                             instanceContext,
                             Cbd->Iopb->TargetFileObject,
                             instanceContext->MetadataOpenTriggerFileObject,
                             FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) );
            }
        } else {


            DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                        ("[Fmm]: FmmReacquireMetadataFileReferences -> Exit without attempting to re-acquire references to metadata handle and file object (InstanceContext = %p, VolumeFileObject = %p, MetadataOpenTriggerFileObject = %p, MetadataAlreadyOpen = 0x%x)\n",
                         instanceContext,
                         Cbd->Iopb->TargetFileObject,
                         instanceContext->MetadataOpenTriggerFileObject,
                         FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) );
        }
    }

    //
    //  Relinquish exclusive access to the instance context
    //

    FmmReleaseResource( &instanceContext->MetadataResouce );


FmmReacquireMetadataFileReferencesCleanup:

    //
    // Release the references we have acquired
    //

    if (instanceContext != NULL) {

        FltReleaseContext( instanceContext );
    }


    return status;;

}



NTSTATUS
FmmSetMetadataOpenTriggerFileObject (
    __inout PFLT_CALLBACK_DATA Cbd
    )
/*++

Routine Description:

    This routine sets the MetadataOpenTriggerFileObject in the instance context
    to the file object of the volume that triggered the release of the metadata file
    references.

Arguments:

    Cbd                 - Supplies a pointer to the callbackData which
                          declares the requested operation.

Return Value:

    Status

Note:

    This routine takes care of the synchronization needed to access the metadata
    file object and handle


--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PFMM_INSTANCE_CONTEXT instanceContext = NULL;

    PAGED_CODE();

    //
    //  Get the instance context
    //

    status = FltGetInstanceContext( Cbd->Iopb->TargetInstance,
                                    &instanceContext );
    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmSetMetadataOpenTriggerFileObject -> Failed to get instance context.\n") );

        goto FmmSetMetadataOpenTriggerFileObjectCleanup;
    }

    //
    //  Acquire exclusive access to the instance context
    //

    FmmAcquireResourceExclusive( &instanceContext->MetadataResouce );

    if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION)) {

        //
        //  If this instance context is in a transition state, it implies that
        //  the instance context lock has been released while sending an operation
        //  down to the file system. The reason for doing so is to prevent a potential
        //  deadlock if an underlying filter sends an IO to the top of the filter
        //  stack while we are holding the resource
        //
        //  We have managed to acquire this resource in this state of transition.
        //  It would be incorrect to use or modify the instance context in any way
        //  in this situation. So we simply let go.
        //

        status = STATUS_FILE_LOCK_CONFLICT;

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmSetMetadataOpenTriggerFileObject -> Failed to get exclusive access to instance context since it is in a state of transition.\n") );
    } else {

        DebugTrace( DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmSetMetadataOpenTriggerFileObject -> Setting MetadataOpenTriggerFileObject to %p (OldValue = %p).\n",
                     Cbd->Iopb->TargetFileObject,
                     instanceContext->MetadataOpenTriggerFileObject) );


        //
        //  Save the volume file object as the trigger file object
        //

        ASSERT((instanceContext->MetadataOpenTriggerFileObject == NULL) ||
               (instanceContext->MetadataOpenTriggerFileObject == Cbd->Iopb->TargetFileObject));

        instanceContext->MetadataOpenTriggerFileObject = Cbd->Iopb->TargetFileObject;
    }

    //
    //  Relinquish exclusive access to the instance context
    //

    FmmReleaseResource( &instanceContext->MetadataResouce);


FmmSetMetadataOpenTriggerFileObjectCleanup:

    //
    // Release the references we have acquired
    //

    if (instanceContext != NULL) {

        FltReleaseContext( instanceContext );
    }


    return status;;
}

__drv_mustHoldCriticalRegion
__drv_releasesCriticalRegion
__drv_maxIRQL(APC_LEVEL)
FORCEINLINE
VOID
FmmBeginFileSystemOperation (
    IN PFMM_INSTANCE_CONTEXT InstanceContext
    )
/*++

Routine Description:

    This routine must be called before the filter performs a file system operation
    if it is holding an exclusive lock to the instance context resource at the
    time it needs to perform the file system operation

Arguments:

    InstanceContext     - Supplies the instance context for this instance.

Return Value:

    Returns the status of this operation.

Note:

    The caller must hold the instance context resource exclusive when this routine is called.

--*/
{
    PAGED_CODE();

    //
    //  Release the instance context lock before sending an operation down to the
    //  file system. The reason for doing so is to prevent a potential deadlock if
    //  an underlying filter sends an IO to the top of the filter stack while we
    //  are holding the resource
    //
    //  Before we release the lock we mark the instance context to indicate it is
    //  in a transition state. Any other thread that finds the instance context in a
    //  transition state will not use or modify the  instance context
    //
    //  This thread can however continue to use/modify the instance context since it
    //  is guaranteed exclusive access. Other threads that see the instance context
    //  in a transition state will not use or modify the context
    //

    ASSERT( !FlagOn( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION ) );

    SetFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION );

    //
    //  Relinquish exclusive access to the instance context
    //

    FmmReleaseResource( &InstanceContext->MetadataResouce);

}

__drv_neverHoldCriticalRegion
__drv_acquiresCriticalRegion
__drv_maxIRQL(APC_LEVEL)
FORCEINLINE
VOID
FmmEndFileSystemOperation (
    IN PFMM_INSTANCE_CONTEXT InstanceContext
    )
/*++

Routine Description:

    This routine must be called after the filter performs a file system operation
    if it was holding an exclusive lock to the instance context resource at the
    time it needed to perform the file system operation

Arguments:

    InstanceContext     - Supplies the instance context for this instance.

Return Value:

    Returns the status of this operation.

Note:

    The caller will hold the instance context resource exclusive when this routine returns.

--*/
{
    PAGED_CODE();

    //
    //  Acquire exclusive access to the instance context
    //

    FmmAcquireResourceExclusive( &InstanceContext->MetadataResouce );


    //
    //  Sanity - nothing should have changed this flag while we dropped the resource
    //  because all other threads will not use or modify the instance context while
    //  this flag is set
    //

    ASSERT( FlagOn( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION ) );


    //
    //  Reset the flag to indicate that the instance context is no longer in
    //  a transition state
    //

    ClearFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION );

}



#if VERIFY_METADATA_OPENED

NTSTATUS
FmmIsMetadataOpen (
    __inout PFLT_CALLBACK_DATA Cbd,
    __out BOOLEAN* MetadataOpen
    )
/*++

Routine Description:

    This routine returns if the metadata file is open on the specified instance.

Arguments:

    Cbd                 - Supplies a pointer to the callbackData which
                          declares the requested operation.
    MetadataOpen        - Returns if the metadata file is open

Return Value:

    Status

Note:

    This routine takes care of the synchronization needed to access the metadata
    file object and handle


--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PFMM_INSTANCE_CONTEXT instanceContext = NULL;


    //
    //  Get the instance context
    //

    status = FltGetInstanceContext( Cbd->Iopb->TargetInstance,
                                    &instanceContext );
    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmIsMetadataOpen -> Failed to get instance context.\n") );

        goto FmmIsMetadataOpenCleanup;
    }

    //
    //  Acquire exclusive access to the instance context
    //

    FmmAcquireResourceShared( &instanceContext->MetadataResouce );

    if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION)) {

        //
        //  If this instance context is in a transition state, it implies that
        //  the instance context lock has been released while sending an operation
        //  down to the file system. The reason for doing so is to prevent a potential
        //  deadlock if an underlying filter sends an IO to the top of the filter
        //  stack while we are holding the resource
        //
        //  We have managed to acquire this resource in this state of transition.
        //  It would be incorrect to use or modify the instance context in any way
        //  in this situation. So we simply let go.
        //

        status = STATUS_FILE_LOCK_CONFLICT;

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmIsMetadataOpen -> Failed to get exclusive access to instance context since it is in a state of transition.\n") );
    } else {

        //
        //  Return if the metadata is opened
        //

        *MetadataOpen = BooleanFlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED );

        //
        //  Sanity - verify that this flag is reflecting the correct state of the metadata file
        //

        ASSERT ( (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED ) &&
                  (instanceContext->MetadataFileObject != NULL) &&
                  (instanceContext->MetadataHandle != NULL)) ||
                 (!FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED ) &&
                  (instanceContext->MetadataFileObject == NULL) &&
                  (instanceContext->MetadataHandle == NULL)) );

    }

    //
    //  Relinquish exclusive access to the instance context
    //

    FmmReleaseResource( &instanceContext->MetadataResouce);


FmmIsMetadataOpenCleanup:

    //
    // Release the references we have acquired
    //

    if (instanceContext != NULL) {

        FltReleaseContext( instanceContext );
    }


    return status;
}


#endif


/*++

Copyright (c) 1999 - 2002  Microsoft Corporation

Module Name:

    operations.c

Abstract:

    This is the support routines module of the kernel mode filter driver implementing
    filter metadata management.


Environment:

    Kernel mode


--*/



#include "pch.h"

//
//  Assign text sections for each routine.
//

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FmmAllocateUnicodeString)
#pragma alloc_text(PAGE, FmmFreeUnicodeString)
#pragma alloc_text(PAGE, FmmTargetIsVolumeOpen)
#pragma alloc_text(PAGE, FmmIsImplicitVolumeLock)
#endif

//
//  Support Routines
//

NTSTATUS
FmmAllocateUnicodeString (
    __inout PUNICODE_STRING String
    )
/*++

Routine Description:

    This routine allocates a unicode string

Arguments:

    String - supplies the size of the string to be allocated in the MaximumLength field
             return the unicode string

Return Value:

    STATUS_SUCCESS                  - success
    STATUS_INSUFFICIENT_RESOURCES   - failure

--*/
{
    PAGED_CODE();

    String->Buffer = ExAllocatePoolWithTag( PagedPool,
                                            String->MaximumLength,
                                            FMM_STRING_TAG );

    if (String->Buffer == NULL) {

        DebugTrace( DEBUG_TRACE_ERROR,
                    ("[Fmm]: Failed to allocate unicode string of size 0x%x\n",
                    String->MaximumLength) );

        return STATUS_INSUFFICIENT_RESOURCES;
    }

    String->Length = 0;

    return STATUS_SUCCESS;
}

VOID
FmmFreeUnicodeString (
    __inout PUNICODE_STRING String
    )
/*++

Routine Description:

    This routine frees a unicode string

Arguments:

    String - supplies the string to be freed

Return Value:

    None

--*/
{
    PAGED_CODE();

    ExFreePoolWithTag( String->Buffer,
                       FMM_STRING_TAG );

    String->Length = String->MaximumLength = 0;
    String->Buffer = NULL;
}


BOOLEAN
FmmTargetIsVolumeOpen (
    __in PFLT_CALLBACK_DATA Cbd
    )
/*++

Routine Description:

    This routine returns if the target object in this callback datastructure
    is a volume.  If the file object is NULL then assume this is NOT a volume 
    file object

Arguments:

    Cbd                   - Supplies a pointer to the callbackData which
                            declares the requested operation.

Return Value:

    TRUE  - target is a volume
    FALSE - target is not a volume

--*/
{
    PAGED_CODE();

    if ((Cbd->Iopb->TargetFileObject != NULL) &&
        FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN )) {

         return TRUE;
    } else {

        return FALSE;
    }
}

NTSTATUS
FmmIsImplicitVolumeLock( 
    __in PFLT_CALLBACK_DATA Cbd,
    __out PBOOLEAN IsLock
    )
/*++

Routine Description:

    This routine determines if an open is a implcit volume lock.

Arguments
    
    Cbd                   - Supplies a pointer to the callbackData which 
                            declares the requested operation.

    IsLock                - Supplies a pointer to a user allocated boolean
                            which is used to tell the user wheather the
                            operation is an implied volume lock.
Return Value:
 
    Returns STATUS_SUCCESS if the the function determined wheather or not
    the operation was a volume lock. On STATUS_SUCCESS it is safe to check 
    IsLock to get the answer. Otherwise, the check failed and we dont know
    if it is a lock or not. STATUS_INVALID_PARAMETER indicates that the
    volume's file system type is unrecognized by the check function. This is
    an error code.

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    PFMM_INSTANCE_CONTEXT instanceContext = NULL;
    USHORT shareAccess;
    ACCESS_MASK prevAccess;

    PAGED_CODE();

    //
    //  Get the instance context so we know 
    //  which file system we are attached to.
    //

    status = FltGetInstanceContext( Cbd->Iopb->TargetInstance,
                                    &instanceContext );
    
    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                    ("[Fmm]: FmmIsImplicitVolumeLock -> Failed to get instance context.\n") );
        goto FmmIsImplicitVolumeLockCleanup;
    }

    ASSERT( instanceContext != NULL );

    //
    //  Now check to see if the open is an implied volume lock
    //  on this filesystem.
    //

    shareAccess = Cbd->Iopb->Parameters.Create.ShareAccess;
    prevAccess = Cbd->Iopb->Parameters.Create.SecurityContext->DesiredAccess;

    switch (instanceContext->FilesystemType) {

        case FLT_FSTYPE_NTFS:
            *IsLock = ((!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE)) &&
                          (BooleanFlagOn( prevAccess,(FILE_WRITE_DATA | FILE_APPEND_DATA) )));
            status = STATUS_SUCCESS;
            break;

        case FLT_FSTYPE_FAT:
            *IsLock = (!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE));
            status = STATUS_SUCCESS;
            break;

        default:
            status = STATUS_INVALID_PARAMETER;
            break;
    }

FmmIsImplicitVolumeLockCleanup:

    if (instanceContext != NULL ) {

        FltReleaseContext( instanceContext );
    }

    return status;
}

/*++

Copyright (c) 1999 - 2002  Microsoft Corporation

Module Name:

    operations.c

Abstract:

    This is the i/o operations module of the kernel mode filter driver implementing
    filter metadata management.


Environment:

    Kernel mode


--*/

#include "pch.h"

//
//  Missing error code on Win2k
//

#if (WINVER==0x0500)
#ifndef STATUS_INVALID_DEVICE_OBJECT_PARAMETER
#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER ((NTSTATUS)0xC0000369L)
#endif
#endif

//
//  Assign text sections for each routine.
//

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FmmPreCreate)
#pragma alloc_text(PAGE, FmmPostCreate)
#pragma alloc_text(PAGE, FmmPreCleanup)
#pragma alloc_text(PAGE, FmmPostCleanup)
#pragma alloc_text(PAGE, FmmPreFSControl)
#pragma alloc_text(PAGE, FmmPostFSControl)
#pragma alloc_text(PAGE, FmmPreDeviceControl)
#pragma alloc_text(NONPAGED, FmmPostDeviceControl)
#pragma alloc_text(PAGE, FmmPreShutdown)
#pragma alloc_text(PAGE, FmmPrePnp)
#pragma alloc_text(PAGE, FmmPostPnp)

#endif

FLT_PREOP_CALLBACK_STATUS
FmmPreCreate (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
{
    NTSTATUS status;
    FLT_PREOP_CALLBACK_STATUS callbackStatus;
    BOOLEAN isImpliedLock;

    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( CompletionContext );

    PAGED_CODE();

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreCreate -> Enter (Cbd = %p, FileObject = %p)\n",
                 Cbd,
                 FltObjects->FileObject) );


    //
    // Initialize defaults
    //

    status = STATUS_SUCCESS;
    callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; // pass through - default is no post op callback

     //
     //  Sanity check to ensure that the volume detection logic works
     //
     //  If the filename length is 0 and the related file object is NULL,
     //  the the FO_VOLUME_OPEN flag must be set
     //

     ASSERT( (!(Cbd->Iopb->TargetFileObject->FileName.Length == 0 &&
                Cbd->Iopb->TargetFileObject->RelatedFileObject == NULL)) ||
              FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN ) );

    if (FmmTargetIsVolumeOpen( Cbd )) {

        //
        //  Check for implicit volume locks (primarily used by autochk)
        //

        status = FmmIsImplicitVolumeLock( Cbd, &isImpliedLock );

        ASSERT( NT_SUCCESS( status ) );

        if (isImpliedLock) {

            //
            //  This is an implicit volume lock
            //

            //
            //  Give up the metadata file handle and the metadata file object
            //

            status = FmmReleaseMetadataFileReferences( Cbd );

            if ( NT_SUCCESS( status )) {

                //
                //  Continue with the lock/dismount - we need to check if the
                //  lock operation suceeded in the post-op
                //

                callbackStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;
            } else {

                //
                //  Fail the lock/dismount
                //

                DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                            ("[Fmm]: Failed to release metadata file references with status 0x%x for a volume lock/dismount\n",
                             status) );

                //
                //  Since this operation has failed, FmmPreCreateCleanup will
                //  update Cbd->IoStatus.Status with the status code and
                //  complete the operation by returning FLT_PREOP_COMPLETE
                //

            }

        }

        //
        //  We do not need to process volume opens any further
        //

         goto FmmPreCreateCleanup;
    }


#if VERIFY_METADATA_OPENED

    //
    //  For all non-volume opens, check if the metadata is open in the post-op
    //
          
    callbackStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;

#endif


    //
    //  Here the filter can do any further processing it may want to do
    //  in the PreCreate Callback
    //



FmmPreCreateCleanup:


    //
    // If any operation has failed then complete and fail the call
    //

    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR,
                    ("[Fmm]: FmmPreCreate -> Failed with status 0x%x \n",
                    status) );

        Cbd->IoStatus.Status = status;
        callbackStatus = FLT_PREOP_COMPLETE;
    }

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreCreate -> Exit (Cbd = %p, FileObject = %p)\n",
                 Cbd,
                 FltObjects->FileObject) );

    return callbackStatus;

}


FLT_POSTOP_CALLBACK_STATUS
FmmPostCreate (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in PVOID CbdContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    )
{
    NTSTATUS status;
    BOOLEAN isImpliedLock;

    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( CbdContext );
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPostCreate -> Enter (Cbd = %p, FileObject = %p)\n",
                 Cbd,
                 FltObjects->FileObject) );

    //
    // Initialize defaults
    //

    status = STATUS_SUCCESS;

    if (!FlagOn(Flags,FLTFL_POST_OPERATION_DRAINING) &&
        FmmTargetIsVolumeOpen( Cbd )) {

        //
        //  Check for implicit volume locks (primarily used by autochk)
        //

        status = FmmIsImplicitVolumeLock( Cbd, &isImpliedLock );

        ASSERT( NT_SUCCESS( status ) );

        if (isImpliedLock) {

            //
            //  This is an implicit volume lock
            //

            if (!NT_SUCCESS( Cbd->IoStatus.Status )) {

                //
                //  The lock failed - reaquire our references to the metadata file
                //  handle and the metadata file object
                //

                status = FmmReacquireMetadataFileReferences( Cbd );

                if (!NT_SUCCESS( status )) {

                    DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                                ("[Fmm]: Failed to re-open metadata with status 0x%x after a failed lock with status 0x%x\n",
                                 status,
                                 Cbd->IoStatus.Status) );

                    //
                    //  Sanity - we are now in a bad state. The lock has failed
                    //  but we have not been able to re-acquire references to
                    //  our metadata file
                    //
                    //  It is always possible to fail with STATUS_INSUFFICIENT_RESOURCES
                    //  so we should ignore that.
                    //
                    //  It is also possible to fail if the instance context was in a transition state
                    //  so we should ignore STATUS_FILE_LOCK_CONFLICT too.
                    //

                    ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) ||
                            (status == STATUS_FILE_LOCK_CONFLICT) );
                }

            } else {

                //
                //  The lock operation suceeded - update the
                //  MetadataOpenTriggerFileObject in the instance context to
                //  the File Object that performed the lock operation.  This
                //  is so we can recognize an implicit unlock at close time.
                //
                //  You may have noticed that we set the
                //  MetadataOpenTriggerFileObject in pre-create and may be
                //  wondering why we set it again in this case.  This is to
                //  support a lower filter doing a recursive lock operation
                //  from the top of the stack.
                //

                status = FmmSetMetadataOpenTriggerFileObject( Cbd );

                if (!NT_SUCCESS( status )) {

                    DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                                ("[Fmm]: Failed to update MetadataOpenTriggerFileObject in the instance context with status 0x%x after a successful lock.\n",
                                 status) );

                    //
                    //  Sanity - we are now in a bad state. We have failed to set the TriggerFileObject
                    //  We may not be able to detect an unlock operation on which we need to
                    //  re-acquire our metadata file references
                    //

                    ASSERT( status == STATUS_FILE_LOCK_CONFLICT );

                }
            }
        }

        //
        //  We do not need to process volume opens any further
        //

        goto FmmPostCreateCleanup;
    }

#if VERIFY_METADATA_OPENED

    //
    //  For all successful non-volume opens, check if the metadata is open 
    //
    
    if (NT_SUCCESS( Cbd->IoStatus.Status )) {

        BOOLEAN metadataOpen;
        
        status = FmmIsMetadataOpen( Cbd, &metadataOpen );

        if (NT_SUCCESS( status) && !metadataOpen) { 

            DebugTrace( DEBUG_TRACE_ERROR,
                        ("[Fmm]: FmmPostCreate -> Create successful but metadata not open \n") );
        }

        ASSERT( ((NT_SUCCESS( status) && metadataOpen) || 
                 (status == STATUS_FILE_LOCK_CONFLICT)) ); 
    }    

#endif

    //
    //  Here the filter can do any further processing it may want to do
    //  in the PostCreate Callback
    //

FmmPostCreateCleanup:


    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR,
                    ("[Fmm]: FmmPostCreate -> Failed with status 0x%x \n",
                    status) );

        //
        //  It does not make sense to fail in the the post op, since the operation has completed
        //
    }


    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPostCreate -> Exit (Cbd = %p, FileObject = %p, Status = 0x%08X)\n",
                 Cbd,
                 FltObjects->FileObject,
                 Cbd->IoStatus.Status) );

    return FLT_POSTOP_FINISHED_PROCESSING;
}


FLT_PREOP_CALLBACK_STATUS
FmmPreCleanup (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
{

    UNREFERENCED_PARAMETER( Cbd );
    UNREFERENCED_PARAMETER( CompletionContext );
    UNREFERENCED_PARAMETER( FltObjects );

    PAGED_CODE();

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreCleanup -> Enter (Cbd = %p, FileObject = %p)\n",
                 Cbd,
                 FltObjects->FileObject) );


    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreCleanup -> Exit (Cbd = %p, FileObject = %p)\n",
                 Cbd,
                 FltObjects->FileObject) );

    return FLT_PREOP_SYNCHRONIZE;
}


FLT_POSTOP_CALLBACK_STATUS
FmmPostCleanup (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __inout PVOID CompletionContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    )
{
    NTSTATUS status;


    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( CompletionContext );
    UNREFERENCED_PARAMETER( Flags );

    //
    //  The pre-operation callback will return FLT_PREOP_SYNCHRONIZE if it needs a
    //  post operation callback. In this case, the Filter Manager will call the
    //  minifilter's post-operation callback in the context of the pre-operation
    //  thread, at IRQL <= apc_level="" this="" allows="" the="" post-operation="" code="" to="" be="" pagable="" and="" also="" allows="" it="" to="" access="" paged="" data="" paged_code="" debugtrace="" debug_trace_all_io="" fmmpostcleanup="" -="" enter="" fileobject="%p)\n'," cbd="" fltobjects-="">FileObject) );

    //
    // Initialize defaults
    //

    status = STATUS_SUCCESS;


    if (!FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) &&
        FmmTargetIsVolumeOpen( Cbd )) {

        if (NT_SUCCESS( Cbd->IoStatus.Status )) {

            //
            //  A close on a volume handle could be an unlock if a lock was
            //  previously  called on this handle. Check if this was a close
            //  on a volume handle on which a lock was previously successful.
            //  If so, re-acquire the references to our metadata file
            //

            status = FmmReacquireMetadataFileReferences( Cbd );

            if (!NT_SUCCESS( status )) {

                DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                            ("[Fmm]: Failed to re-open metadata with status 0x%x after a successful unlock.\n",
                             status) );

                //
                //  Sanity - we are now in a bad state. The volume was unlocked
                //  but we have not been able to re-acquire references to
                //  our metadata file
                //
                //  Ntfs dismounts and remounts the volume after an unlock. So we ignore
                //  failures to open the metadata with STATUS_INVALID_DEVICE_OBJECT_PARAMETER
                //  or STATUS_FILE_INVALID because the volume should have been remounted and 
                //  the metadata file should have been opened on the newly mounted instance 
                //  of that volume
                //
                //  Note however, that if this is an implicit lock (used by autoXXX.exe) then
                //  ntfs will not automatically dismount the volume. It relies on the application
                //  to restart the system if it has made any changes to the volume. If the
                //  application has not made any changes then ntfs will simply continue on
                //  after the unlock without dismounting the volume. Hence we cannot assume
                //  that ntfs always dismounts the volume. We need to try to re-acquire
                //  a handle to our metadata file and ignore failure with error
                //  STATUS_INVALID_DEVICE_OBJECT_PARAMETER or STATUS_NO_MEDIA_IN_DEVICE which 
                //  indicate that the volume has been dismounted
                //
                //  Also it is always possible to fail with STATUS_INSUFFICIENT_RESOURCES
                //  so we should ignore that as well.
                //
                //  It is also possible to fail if the instance context was in a transition state
                //  so we should ignore STATUS_FILE_LOCK_CONFLICT too.
                //

                ASSERT( (status == STATUS_INVALID_DEVICE_OBJECT_PARAMETER) ||
                        (status == STATUS_NO_MEDIA_IN_DEVICE) ||
                        (status == STATUS_INSUFFICIENT_RESOURCES) ||
                        (status == STATUS_FILE_LOCK_CONFLICT) ||
                        (status == STATUS_FILE_INVALID) );

                //
                //  There is little use updating the return status since it already has a
                //  failure code from the failed dismount
                //
            }
        }

        //
        //  We don't need to process a volume CleanUp any further
        //

        goto FmmPostCleanupCleanup;
    }


    //
    //  Here the filter can do any further processing it may want to do
    //  in the PostCleanUp Callback
    //

FmmPostCleanupCleanup:

    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR,
                    ("[Fmm]: FmmPostCleanup -> Failed with status 0x%x \n",
                    status) );

        //
        //  It does not make sense to fail in the the post op, since the operation has completed
        //

    }


    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPostCleanup -> Exit (Cbd = %p, FileObject = %p)\n",
                 Cbd,
                 FltObjects->FileObject) );

    return FLT_POSTOP_FINISHED_PROCESSING;
}


FLT_PREOP_CALLBACK_STATUS
FmmPreFSControl (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
{

    NTSTATUS status;
    FLT_PREOP_CALLBACK_STATUS callbackStatus;

    UNREFERENCED_PARAMETER( CompletionContext );
    UNREFERENCED_PARAMETER( FltObjects );

    PAGED_CODE();

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreFsCtl -> Enter (FsControlCode = 0x%x, Cbd = %p, FileObject = %p)\n",
                 Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode,
                 Cbd,
                 FltObjects->FileObject) );


    //
    //  default to no post-op callback
    //

    callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;


    if (Cbd->Iopb->MinorFunction != IRP_MN_USER_FS_REQUEST) {

        goto FmmPreFSControlCleanup;
    }


    switch (Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode) {

    //
    //  System FSCTLs that we are interested in
    //

    case FSCTL_DISMOUNT_VOLUME:
    case FSCTL_LOCK_VOLUME:


        if (FmmTargetIsVolumeOpen( Cbd )) {

            //
            //  Give up the metadata file handle and the metadata file object
            //

            status = FmmReleaseMetadataFileReferences( Cbd );

            if ( NT_SUCCESS( status )) {

                //
                //  Continue with the lock/dismount - we need to check if the
                //  lock operation suceeded in the post-op
                //

                callbackStatus = FLT_PREOP_SYNCHRONIZE;
            } else {

                //
                //  Fail the lock/dismount
                //

                DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                            ("[Fmm]: Failed to release metadata file references with status 0x%x for a volume lock/dismount\n",
                             status) );

                Cbd->IoStatus.Status = status;
                callbackStatus = FLT_PREOP_COMPLETE;
            }
        }
        break;

    case FSCTL_UNLOCK_VOLUME:

        //
        //  We need to handle unlock in the post-op
        //

        callbackStatus = FLT_PREOP_SYNCHRONIZE;
        break;

    }

FmmPreFSControlCleanup:


    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreFsCtl -> Exit (FsControlCode = 0x%x, Cbd = %p, FileObject = %p)\n",
                 Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode,
                 Cbd,
                 FltObjects->FileObject) );

    return callbackStatus;

}


FLT_POSTOP_CALLBACK_STATUS
FmmPostFSControl (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in PVOID CompletionContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    )
{

    NTSTATUS status;

    UNREFERENCED_PARAMETER( CompletionContext );
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPostFsCtl -> Enter (FsControlCode = 0x%x, Cbd = %p, FileObject = %p)\n",
                 Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode,
                 Cbd,
                 FltObjects->FileObject) );


    if (!FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING )) {

        switch (Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode) {

        //
        //  System FSCTLs that we are interested in
        //

        case FSCTL_DISMOUNT_VOLUME:

            if (FmmTargetIsVolumeOpen( Cbd )) {

                if (NT_SUCCESS( Cbd->IoStatus.Status )) {

                    //
                    //  Dismount succeeded - teardown our instance because its no longer valid.
                    //  If we do not tear down this instance, it will stay around until the
                    //  last handle for that volume is closed. This will cause the instance
                    //  enumeration APIs to see multiple instances
                    //

                    status = FltDetachVolume( Globals.Filter, FltObjects->Volume, NULL );

                    if (!NT_SUCCESS( status )) {

                        DebugTrace( DEBUG_TRACE_ERROR,
                                    ("[Fmm]: Failed to detach instance with status 0x%x after a volume dismount\n",
                                     status) );

                        //
                        //  Doesn't make sense to update the status code in the post-op with a
                        //  failure code since the operation has already been performed by the
                        //  file system
                        //
                    }

                } else {

                    //
                    //  The dismount failed - reaquire our references to the metadata file
                    //  handle and the metadata file object
                    //

                    status = FmmReacquireMetadataFileReferences( Cbd );

                    if (!NT_SUCCESS( status )) {

                        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                                    ("[Fmm]: Failed to re-open metadata with status 0x%x after a failed dismount with status 0x%x\n",
                                     status,
                                     Cbd->IoStatus.Status) );

                        //
                        //  Sanity - we are now in a bad state. The dismount has failed
                        //  but we have not been able to re-acquire references to
                        //  our metadata file
                        //
                        //  It is always possible to fail with STATUS_INSUFFICIENT_RESOURCES
                        //  so we should ignore that.
                        //
                        //  It is also possible to fail if the instance context was in a transition state
                        //  so we should ignore STATUS_FILE_LOCK_CONFLICT too.
                        //

                        ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) ||
                                (status == STATUS_FILE_LOCK_CONFLICT) );

                        //
                        //  There is little use updating the return status since it already has a
                        //  failure code from the failed dismount
                        //
                    }
                }
            }

            break;

        case FSCTL_LOCK_VOLUME:


            if (FmmTargetIsVolumeOpen( Cbd )) {

                if (!NT_SUCCESS( Cbd->IoStatus.Status )) {

                    //
                    //  The lock failed - reaquired our references to the metadata file
                    //  handle and the metadata file object
                    //

                    status = FmmReacquireMetadataFileReferences( Cbd );

                    if (!NT_SUCCESS( status )) {

                        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                                    ("[Fmm]: Failed to re-open metadata with status 0x%x after a failed lock with status 0x%x\n",
                                     status,
                                     Cbd->IoStatus.Status) );

                        //
                        //  Sanity - we are now in a bad state. The lock has failed
                        //  but we have not been able to re-acquire references to
                        //  our metadata file
                        //
                        //  It is always possible to fail with STATUS_INSUFFICIENT_RESOURCES
                        //  so we should ignore that.
                        //
                        //  It is also possible to fail if the instance context was in a transition state
                        //  so we should ignore STATUS_FILE_LOCK_CONFLICT too.
                        //

                        ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) ||
                                (status == STATUS_FILE_LOCK_CONFLICT) );

                        //
                        //  There is little use updating the return status since it already has a
                        //  failure code from the failed lock
                        //

                    }
                } else {

                    //
                    //  The lock operation suceeded - update the MetadataOpenTriggerFileObject in the
                    //  instance context to the File Object on the lock operation suceeded because this
                    //  the file object on close/unlock of which, we need to reacquire our metadata file
                    //  references
                    //

                    status = FmmSetMetadataOpenTriggerFileObject( Cbd );

                    if (!NT_SUCCESS( status )) {

                        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                                    ("[Fmm]: Failed to update MetadataOpenTriggerFileObject in the instance context with status 0x%x after a successful lock.\n",
                                     status) );

                        //
                        //  Sanity - we are now in a bad state. We have failed to
                        //  set the TriggerFileObject We may not be able to detect
                        //  an unlock operation on which we need to re-acquire our
                        //  metadata file references
                        //

                        ASSERT( status == STATUS_FILE_LOCK_CONFLICT );

                        //
                        //  Doesn't make sense to update the status code in the
                        //  post-op with a failure code since the operation has
                        //  already been performed by the file system
                        //
                    }

                }
            }

            break;

        case FSCTL_UNLOCK_VOLUME:

            if (NT_SUCCESS( Cbd->IoStatus.Status )) {

                //
                //  The unlock suceeded - reaquired our references to the metadata file
                //  handle and the metadata file object
                //

                status = FmmReacquireMetadataFileReferences( Cbd );

                if (!NT_SUCCESS( status )) {

                    DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                                ("[Fmm]: Failed to re-open metadata with status 0x%x after a successful unlock.\n",
                                 status) );

                    //
                    //  Sanity - we are now in a bad state. The volume was unlocked
                    //  but we have not been able to re-acquire references to
                    //  our metadata file
                    //
                    //  Ntfs dismounts and remounts the volume after an unlock. So we ignore
                    //  failures to open the metadata with STATUS_INVALID_DEVICE_OBJECT_PARAMETER
                    //  because the volume should have been remounted and the metadata file
                    //  should have been opened on the newly mounted instance of that volume
                    //
                    //  Note however, that if this is an implicit lock (used by autoXXX.exe) then
                    //  ntfs will not automatically dismount the volume. It relies on the application
                    //  to restart the system if it has made any changes to the volume. If the
                    //  application has not made any changes then ntfs will simply continue on
                    //  after the unlock without dismounting the volume. Hence we cannot assume
                    //  that ntfs always dismounts the volume. We need to try to re-acquire
                    //  a handle to our metadata file and ignore failure with error
                    //  STATUS_INVALID_DEVICE_OBJECT_PARAMETER which indicates that the
                    //  volume has dismounted
                    //
                    //  Also it is always possible to fail with STATUS_INSUFFICIENT_RESOURCES
                    //  so we should ignore that as well.
                    //
                    //  It is also possible to fail if the instance context was in a transition state
                    //  so we should ignore STATUS_FILE_LOCK_CONFLICT too.
                    //

                    ASSERT( (status == STATUS_INVALID_DEVICE_OBJECT_PARAMETER) ||
                            (status == STATUS_INSUFFICIENT_RESOURCES) ||
                            (status == STATUS_FILE_LOCK_CONFLICT) );

                    //
                    //  There is little use updating the return status since it already has a
                    //  failure code from the failed dismount
                    //
                }
            }


            break;

        }
    }

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPostFsCtl -> Exit (FsControlCode = 0x%x, Cbd = %p, FileObject = %p)\n",
                 Cbd->Iopb->Parameters.FileSystemControl.Common.FsControlCode,
                 Cbd,
                 FltObjects->FileObject) );


    return FLT_POSTOP_FINISHED_PROCESSING;
}


FLT_PREOP_CALLBACK_STATUS
FmmPreDeviceControl (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
{

    NTSTATUS status = STATUS_SUCCESS;
    FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
    PFMM_INSTANCE_CONTEXT instanceContext = NULL;

    UNREFERENCED_PARAMETER( FltObjects );

    PAGED_CODE();

    *CompletionContext = NULL;


    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreDeviceControl -> Enter (IoControlCode = 0x%x, Cbd = %p, FileObject = %p)\n",
                 Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode,
                 Cbd,
                 FltObjects->FileObject) );


    switch (Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode) {

    //
    //  System IOCTLs that we are interested in
    //

    case IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES:

        //
        //  We want the snapshot to have a consistent image
        //  of our metadata file that is in sync with the state
        //  of the volume
        //
    
        //
        //  Get the instance context
        //

        status = FltGetInstanceContext( Cbd->Iopb->TargetInstance,
                                        &instanceContext );
        if (!NT_SUCCESS( status )) {

            DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                        ("[Fmm]: Failed to get instance context in FmmPreDeviceControl.\n") );

            goto FmmPreDeviceControlCleanup;
        }


        //
        //  Here the filter must flush any portion of its metadata
        //  that it has not flushed to disk
        //
        //  If the filter is using mapped cache buffers to read/write
        //  its metadata then the File System will take care of all the
        //  flushing. The filter just needs to ensure that it does not
        //  write to any of its mapped cache buffers while the FS is
        //  trying to flush changes out to disk.
        //

        //
        //  After this point, the filter should not be sending any updates 
        //  to its metadata file on disk until the post-op callback for 
        //  IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES 
        //
        //  The filter would do this by marking its instance context in some way
        //  (say, by setting a flag) to indicate to other threads that they should 
        //  not try to update the metadata file on the disk
        // 

        //
        //  Do not release the instance context but instead pass it to the PostOp
        //  The PostOp routine would need to unmark the instance context in some way
        //  to indicate that it is now ok to update the metadata file on the disk.
        //
        //  Since we do not want to fail this unmarking because we cannot acquire the
        //  instance context in the post-op, it is better to pass the instance context from
        //  PreOp to PostOp
        //

        *CompletionContext = instanceContext;

        //
        //  Force a post-op so we may undo our marking and release the instance context 
        //

        callbackStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK;

        break;

    }

FmmPreDeviceControlCleanup:


    //
    // If any operation has failed then complete and fail the call
    //

    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR,
                    ("[Fmm]: FmmPreDeviceControl -> Failed with status 0x%x \n",
                    status) );

        //
        //  We are not having a post-op since the pre-op failed
        //  Release the instance context
        //

        if (instanceContext != NULL) {

            FltReleaseContext( instanceContext );
            *CompletionContext = NULL;
        }

        Cbd->IoStatus.Status = status;
        callbackStatus = FLT_PREOP_COMPLETE;
    }

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreDeviceControl -> Exit (IoControlCode = 0x%x, Cbd = %p, FileObject = %p)\n",
                 Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode,
                 Cbd,
                 FltObjects->FileObject) );

    return callbackStatus;

}


FLT_POSTOP_CALLBACK_STATUS
FmmPostDeviceControl (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in PVOID CbdContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    )
{

    PFMM_INSTANCE_CONTEXT instanceContext = NULL;

    UNREFERENCED_PARAMETER( Flags );
    UNREFERENCED_PARAMETER( FltObjects );


    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPostDeviceControl -> Enter (IoControlCode = 0x%x, Cbd = %p, FileObject = %p)\n",
                 Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode,
                 Cbd,
                 FltObjects->FileObject) );

    //
    //  We need to do this even if we are draining
    //

    switch (Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode) {

    //
    //  System IOCTLs that we are interested in
    //

    case IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES:

        //
        //  Assign the instance context
        //

        instanceContext = (PFMM_INSTANCE_CONTEXT) CbdContext;

        //
        //  Sanity
        //

        ASSERT( instanceContext != NULL );


        //
        //  At this point, it is ok for the filter to send updates to its metadata
        //  file on disk
        //
        //  The filter would do this by unmarking its instance context (say by,
        //  resetting the flag that it set in the PreOp) to indicate to other threads 
        //  that it is now ok to update the metadata file on the disk
        // 


        //
        //  Release the instance context
        //

        FltReleaseContext( instanceContext );

        break;

    }

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPostDeviceControl -> Exit (IoControlCode = 0x%x, Cbd = %p, FileObject = %p)\n",
                 Cbd->Iopb->Parameters.DeviceIoControl.Common.IoControlCode,
                 Cbd,
                 FltObjects->FileObject) );


    return FLT_POSTOP_FINISHED_PROCESSING;
}



FLT_PREOP_CALLBACK_STATUS
FmmPreShutdown (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
{
    NTSTATUS status;

    UNREFERENCED_PARAMETER( CompletionContext );
    UNREFERENCED_PARAMETER( Cbd );

    PAGED_CODE();

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreShutdown -> Enter (Cbd = %p, FileObject = %p, Volume = %p)\n",
                 Cbd,
                 FltObjects->FileObject,
                 FltObjects->Volume) );

    status = FltDetachVolume( Globals.Filter, FltObjects->Volume, NULL );

    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR,
                    ("[Fmm]: Failed to detach instance with status 0x%x on system shutdown\n",
                     status) );

        //
        //  Doesn't really make sense to fail a shutdown, even if this operation failed
        //

    }

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPreShutdown -> Exit (Cbd = %p, FileObject = %p, Volume = %p)\n",
                 Cbd,
                 FltObjects->FileObject,
                 FltObjects->Volume) );

    return FLT_PREOP_SUCCESS_NO_CALLBACK;
}


FLT_PREOP_CALLBACK_STATUS
FmmPrePnp (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __deref_out_opt PVOID *CompletionContext
    )
/*

Routine Description:

    This routine handles the pre-processing of all PNP operations received
    on this instance. It handles query, cancel and suprise device removal.
    For query device removal we have to close all open file handles we hold
    so that the base file system can correctly response to these PNP requests.

Arguments:

    Cbd - Pointer to the FLT_CALLBACK_DATA structure containing all the relevant
        parameters for this operation.

    FltObject - Pointer to the FLT_RELATED_OBJECTS data structure containing,
        opaque handles to this filter, instance and its associated volume.

    CompletionContext - Not used.

Return Value:

    FLT_PREOP_SUCCESS_NO_CALLBACK as we are done with our processing and are
    not interested in a post-operartion callback.

*/
{
    NTSTATUS status;
    FLT_PREOP_CALLBACK_STATUS callbackStatus;

    UNREFERENCED_PARAMETER( CompletionContext );

    PAGED_CODE();

    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPrePnp -> Enter (Cbd = %p, FileObject = %p, Volume = %p)\n",
                 Cbd,
                 FltObjects->FileObject,
                 FltObjects->Volume) );

    //
    //  default to no post op callback
    //

    callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;

    switch (Cbd->Iopb->MinorFunction) {

    case IRP_MN_QUERY_REMOVE_DEVICE:

        //
        //  Give up the metadata file handle and the metadata file object
        //

        status = FmmReleaseMetadataFileReferences( Cbd );

        if (!NT_SUCCESS( status )) {

            //
            // Fail the query removal
            //

            DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                        ("[Fmm]: Failed to release metadata file references with status 0x%x for query removal\n",
                         status) );

            Cbd->IoStatus.Status = status;
            callbackStatus = FLT_PREOP_COMPLETE;
        }

        break;

    case IRP_MN_CANCEL_REMOVE_DEVICE:

        //
        //  We need to pass this notification through to the file system
        //  so he start allowing IO to the volume. We file for a post op
        //  so that we can reacquire our resources. We must return 
        //  FLT_PREOP_SYNCRONIZE because we need to be below DPC inorder
        //  to reopen our metadata file.
        //

        callbackStatus = FLT_PREOP_SYNCHRONIZE;

        break;

    case IRP_MN_SURPRISE_REMOVAL:

        //
        //  Teardown our instance because its no longer valid.
        //

        status = FltDetachVolume( Globals.Filter, FltObjects->Volume, NULL );

        if (!NT_SUCCESS( status )) {

            DebugTrace( DEBUG_TRACE_ERROR,
                        ("[Fmm]: Failed to detach instance with status 0x%x after a surprise removal\n",
                         status) );

        }

        break;

    default:

        //
        //  Pass all PNP minor codes we don't care about.
        //

        break;
    }


    DebugTrace( DEBUG_TRACE_ALL_IO,
                ("[Fmm]: FmmPrePnp -> Exit (Cbd = %p, FileObject = %p, Volume = %p)\n",
                 Cbd,
                 FltObjects->FileObject,
                 FltObjects->Volume) );

    return callbackStatus;
}

FLT_POSTOP_CALLBACK_STATUS
FmmPostPnp (
    __inout PFLT_CALLBACK_DATA Cbd,
    __in PCFLT_RELATED_OBJECTS FltObjects,
    __in PVOID CbdContext,
    __in FLT_POST_OPERATION_FLAGS Flags
    )
/*

Routine Description:
    
    This routine handles the post-processing of IRP_MN_CANCEL_REMOVE_DEVICE.
    We reacquire our references to the metadata file handle and the metadata 
    file object.

*/
{


    NTSTATUS status;

    UNREFERENCED_PARAMETER( CbdContext );
    UNREFERENCED_PARAMETER( FltObjects );

    PAGED_CODE();

    //
    //  Sanity - we should only have a post operation for IRP_MN_CANCEL_DEVICE.
    //
    
    ASSERT( Cbd->Iopb->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE );

    //
    //  Sanity - IRP_MN_CANCEL_DEVICE cannot fail.
    //

    ASSERT( Cbd->IoStatus.Status == STATUS_SUCCESS );

    if( FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) ) {

        //
        //  We are draining. This means that we should not reacquire our 
        //  resources because the IRP may not have completed.
        //

        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    //
    //  The device removal was cancelled - reaquire our references to the
    //  metadata file handle and the metadata file object
    //

    status = FmmReacquireMetadataFileReferences( Cbd );

    if (!NT_SUCCESS( status )) {

        DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_METADATA_OPERATIONS,
                ("[Fmm]: Failed to re-open metadata with status 0x%x after cancel device removal.\n",
                 status) );

        //
        //  Sanity - we are now in a bad state. The removal has been
        //  cancelled but we have not been able to re-acquire references
        //  to our metadata file
        //
        //  It is always possible to fail with STATUS_INSUFFICIENT_RESOURCES
        //  so we should ignore that.
        //
        //  It is also possible to fail if the instance context was in a transition state
        //  so we should ignore STATUS_FILE_LOCK_CONFLICT too.
        //

        ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) ||
                (status == STATUS_FILE_LOCK_CONFLICT) );

    }
    
    return FLT_POSTOP_FINISHED_PROCESSING;
}


     
     
     
     
    
    
   
   
  
  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值