Sample Code

Windows Driver Samples/ CancelSafe File System Minifilter Driver/ C++/ cancelSafe.c/

/*++

Copyright (c) 1999 - 2002  Microsoft Corporation

Module Name:

    cancelSafe.c

Abstract:

    This is the main module of the cancelSafe miniFilter driver.

Environment:

    Kernel mode

--*/

#include <fltKernel.h>
#include <dontuse.h>
#include <suppress.h>


//
//  Debug flags and helper functions
//

#define CSQ_TRACE_ERROR                     0x00000001
#define CSQ_TRACE_LOAD_UNLOAD               0x00000002
#define CSQ_TRACE_INSTANCE_CALLBACK         0x00000004
#define CSQ_TRACE_CONTEXT_CALLBACK          0x00000008
#define CSQ_TRACE_CBDQ_CALLBACK             0x00000010
#define CSQ_TRACE_PRE_READ                  0x00000020
#define CSQ_TRACE_ALL                       0xFFFFFFFF

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

//
//  Memory Pool Tags
//

#define INSTANCE_CONTEXT_TAG              'IqsC'
#define QUEUE_CONTEXT_TAG                 'QqsC'
#define CSQ_REG_TAG                       'RqsC'
#define CSQ_STRING_TAG                    'SqsC'

//
// Registry value names and default values
//

#define CSQ_DEFAULT_TIME_DELAY            150000000
#define CSQ_DEFAULT_MAPPING_PATH          L"\\"
#define CSQ_KEY_NAME_DELAY                L"OperatingDelay"
#define CSQ_KEY_NAME_PATH                 L"OperatingPath"
#define CSQ_KEY_NAME_DEBUG_LEVEL          L"DebugLevel"
#define CSQ_MAX_PATH_LENGTH               256


//
//  Prototypes
//

//
//  Queue context data structure
//

typedef struct _QUEUE_CONTEXT {

    FLT_CALLBACK_DATA_QUEUE_IO_CONTEXT CbdqIoContext;

} QUEUE_CONTEXT, *PQUEUE_CONTEXT;

//
//  Instance context data structure
//

typedef struct _INSTANCE_CONTEXT {

    //
    //  Instance for this context.
    //

    PFLT_INSTANCE Instance;

    //
    //  Cancel safe queue members
    //

    FLT_CALLBACK_DATA_QUEUE Cbdq;
    LIST_ENTRY QueueHead;
    FAST_MUTEX Lock;

    //
    //  Flag to control the life/death of the work item thread
    //

    volatile LONG WorkerThreadFlag;

    //
    //  Notify the worker thread that the instance is being torndown
    //

    KEVENT TeardownEvent;

} INSTANCE_CONTEXT, *PINSTANCE_CONTEXT;


typedef struct _CSQ_GLOBAL_DATA {

    ULONG  DebugLevel;

    PFLT_FILTER  FilterHandle;

    NPAGED_LOOKASIDE_LIST QueueContextLookaside;

    UNICODE_STRING MappingPath;

    PWSTR PathBuffer;

    LONGLONG TimeDelay;
    
} CSQ_GLOBAL_DATA;



//
//  Global variables
//

CSQ_GLOBAL_DATA Globals;


//
//  Local function prototypes
//

DRIVER_INITIALIZE DriverEntry;
NTSTATUS
DriverEntry (
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    );

VOID
FreeGlobals(
    );

NTSTATUS
Unload (
    _In_ FLT_FILTER_UNLOAD_FLAGS Flags
    );

VOID
ContextCleanup (
    _In_ PFLT_CONTEXT Context,
    _In_ FLT_CONTEXT_TYPE ContextType
    );

NTSTATUS
InstanceSetup (
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
    _In_ DEVICE_TYPE VolumeDeviceType,
    _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
    );

NTSTATUS
InstanceQueryTeardown (
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
    );

VOID
InstanceTeardownStart (
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
    );

VOID
InstanceTeardownComplete (
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
    );

NTSTATUS
SetConfiguration (
    _In_ PUNICODE_STRING RegistryPath
    );

VOID
_IRQL_requires_max_(APC_LEVEL)
_IRQL_raises_(APC_LEVEL)
_Requires_lock_not_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
_Acquires_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
CsqAcquire(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _Out_ PKIRQL Irql
    );

VOID
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(APC_LEVEL)
_IRQL_raises_(PASSIVE_LEVEL)
_Requires_lock_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
_Releases_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
CsqRelease(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _In_ KIRQL Irql
    );

NTSTATUS
CsqInsertIo(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _In_ PFLT_CALLBACK_DATA Data,
    _In_opt_ PVOID Context
    );
VOID
CsqRemoveIo(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _In_ PFLT_CALLBACK_DATA Data
    );
PFLT_CALLBACK_DATA
CsqPeekNextIo(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _In_opt_ PFLT_CALLBACK_DATA Data,
    _In_opt_ PVOID PeekContext
    );
VOID
CsqCompleteCanceledIo(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _Inout_ PFLT_CALLBACK_DATA Data
    );

FLT_PREOP_CALLBACK_STATUS
PreRead (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    );

VOID
PreReadWorkItemRoutine(
    _In_ PFLT_GENERIC_WORKITEM WorkItem,
    _In_ PFLT_FILTER Filter,
    _In_ PVOID Context
    );

NTSTATUS
PreReadPendIo(
    _In_ PINSTANCE_CONTEXT InstanceContext
    );

NTSTATUS
PreReadProcessIo(
    _Inout_ PFLT_CALLBACK_DATA Data
    );

VOID
PreReadEmptyQueueAndComplete(
    _In_ PINSTANCE_CONTEXT InstanceContext
    );

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

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, SetConfiguration)
#pragma alloc_text(PAGE, Unload)
#pragma alloc_text(PAGE, FreeGlobals)
#pragma alloc_text(PAGE, ContextCleanup)
#pragma alloc_text(PAGE, InstanceSetup)
#pragma alloc_text(PAGE, InstanceQueryTeardown)
#pragma alloc_text(PAGE, InstanceTeardownStart)
#pragma alloc_text(PAGE, InstanceTeardownComplete)

#endif

//
//  Filters callback routines
//

FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_READ,
      FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO,
      PreRead,
      NULL },

    { IRP_MJ_OPERATION_END }
};

//
// Filters context registration data structure
//

const FLT_CONTEXT_REGISTRATION ContextRegistration[] = {

    { FLT_INSTANCE_CONTEXT,
        0,
        ContextCleanup,
        sizeof( INSTANCE_CONTEXT ),
        INSTANCE_CONTEXT_TAG },

    { FLT_CONTEXT_END }
};

//
// Filters registration data structure
//

FLT_REGISTRATION FilterRegistration = {

    sizeof( FLT_REGISTRATION ),             //  Size
    FLT_REGISTRATION_VERSION,               //  Version
    0,                                      //  Flags
    ContextRegistration,                    //  Context
    Callbacks,                              //  Operation callbacks
    Unload,                                 //  Filters unload routine
    InstanceSetup,                          //  InstanceSetup routine
    InstanceQueryTeardown,                  //  InstanceQueryTeardown routine
    InstanceTeardownStart,                  //  InstanceTeardownStart routine
    InstanceTeardownComplete,               //  InstanceTeardownComplete routine
    NULL, NULL, NULL                        //  Unused naming support callbacks
};

//
//  Filter driver initialization and unload routines
//

NTSTATUS
DriverEntry (
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:

    This is the initialization routine for this filter driver. It registers
    itself with the filter manager and initializes all its global data structures.

Arguments:

    DriverObject - Pointer to driver object created by the system to
        represent this driver.

    RegistryPath - Unicode string identifying where the parameters for this
        driver are located in the registry.

Return Value:

    Returns STATUS_SUCCESS.

--*/
{
    NTSTATUS Status;

    //
    //  Default to NonPagedPoolNx for non paged pool allocations where supported.
    //
    
    ExInitializeDriverRuntime( DrvRtPoolNxOptIn );

    //
    //  Initialize global lookaside list
    //

    ExInitializeNPagedLookasideList( &Globals.QueueContextLookaside,
                                     NULL,
                                     NULL,
                                     0,
                                     sizeof( QUEUE_CONTEXT ),
                                     QUEUE_CONTEXT_TAG,
                                     0 );

    //
    //  Initialize the configuration to default values
    //
    
    Globals.DebugLevel = CSQ_TRACE_ERROR;        

    Globals.TimeDelay = CSQ_DEFAULT_TIME_DELAY;

    Globals.PathBuffer = NULL;

    RtlInitUnicodeString( &Globals.MappingPath, CSQ_DEFAULT_MAPPING_PATH );


    //
    //  Modify the configuration based on values in the registry
    //

    Status = SetConfiguration( RegistryPath );

    if (!NT_SUCCESS( Status )) {
        
        goto DriverEntryCleanup;
    }

    DebugTrace( CSQ_TRACE_LOAD_UNLOAD,
                ("[Csq]: CancelSafe!DriverEntry\n") );



    //
    //  Register with the filter manager
    //

    Status = FltRegisterFilter( DriverObject,
                                &FilterRegistration,
                                &Globals.FilterHandle );

    if (!NT_SUCCESS( Status )) {

        DebugTrace( CSQ_TRACE_LOAD_UNLOAD | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to register filter (Status = 0x%x)\n",
                    Status) );

        goto DriverEntryCleanup;

    }

    //
    //  Start filtering I/O
    //

    Status = FltStartFiltering( Globals.FilterHandle );

    if (!NT_SUCCESS( Status )) {

        DebugTrace( CSQ_TRACE_LOAD_UNLOAD | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to start filtering (Status = 0x%x)\n",
                    Status) );

        FltUnregisterFilter( Globals.FilterHandle );

        goto DriverEntryCleanup;

    }


    DebugTrace( CSQ_TRACE_LOAD_UNLOAD,
                ("[Csq]: Driver loaded complete\n") );

DriverEntryCleanup:

    if (!NT_SUCCESS( Status )) {
        
        FreeGlobals();
    }
    
    return Status;
}


NTSTATUS
SetConfiguration (
    _In_ PUNICODE_STRING RegistryPath
    ) 
/*++

Routine Description:

    This routine tries to configure the debuglevel, mapping path and
    queue delay based on values in the registry.

Arguments:

    RegistryPath - The path key passed to the driver during DriverEntry.

Return Value:

    STATUS_SUCCESS if the function completes successfully.  Otherwise a valid
    NTSTATUS code is returned.

--*/
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES Attributes;
    HANDLE DriverRegKey = NULL;
    UNICODE_STRING ValueName;
    BOOLEAN CloseHandle = FALSE;
    UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + CSQ_MAX_PATH_LENGTH  * sizeof(WCHAR)];
    PKEY_VALUE_PARTIAL_INFORMATION Value = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
    ULONG ValueLength = sizeof(Buffer);
    ULONG ResultLength;
    ULONG Length;

    //
    //  Open the driver registry key.
    //

    InitializeObjectAttributes( &Attributes,
                                RegistryPath,
                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL );

    Status = ZwOpenKey( &DriverRegKey,
                        KEY_READ,
                        &Attributes );

    if (!NT_SUCCESS( Status )) {

        goto SetConfigurationCleanup;
    }

    CloseHandle = TRUE;
    
    //
    //  Query the debug level
    //

    RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_DEBUG_LEVEL );
    
    Status = ZwQueryValueKey( DriverRegKey,
                              &ValueName,
                              KeyValuePartialInformation,
                              Value,
                              ValueLength,
                              &ResultLength );
    
    if (NT_SUCCESS( Status )) {

        Globals.DebugLevel = *(PULONG)(Value->Data);
    }

    
    //
    //  Query the queue time delay
    //

 
    RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_DELAY );

    Status = ZwQueryValueKey( DriverRegKey,
                              &ValueName,
                              KeyValuePartialInformation,
                              Value,
                              ValueLength,
                              &ResultLength );

    if (NT_SUCCESS( Status )) {

        if (Value->Type != REG_DWORD) {

            Status = STATUS_INVALID_PARAMETER;
            goto SetConfigurationCleanup;
        }
        
        Globals.TimeDelay = (LONGLONG)(*(PULONG)(Value->Data));
        
    }
  
    //
    // Query the mapping path
    //

    RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_PATH );

    //
    //  For simplicity of this sample, the length of the mapping path
    //  allowed in the registry is limited to CSQ_MAX_PATH_LENGTH
    //  characters. If this size is exceeded the default mapping path
    //  will be used.
    //

    Status = ZwQueryValueKey( DriverRegKey,
                              &ValueName,
                              KeyValuePartialInformation,
                              Value,
                              ValueLength,
                              &ValueLength );

    if (NT_SUCCESS( Status )) {

        //
        //  Set up the mapping and ensure the mapping string format is "\a\...\".
        //  If the mapping path doesn't begin with '\' fail, if it doesn't end
        //  with a '\' append one.
        //

        if (*(PWCHAR)(Value->Data) != L'\\') {

            Status = STATUS_INVALID_PARAMETER;
            goto SetConfigurationCleanup;
        }

        //
        //  Allocate enough space for an extra character in case a trailing '\'
        //  is missing and needs to be added.
        //
        
        Length = Value->DataLength + sizeof(WCHAR),

        Globals.PathBuffer = ExAllocatePoolWithTag( NonPagedPool, Length, CSQ_STRING_TAG );
        
        if (Globals.PathBuffer == NULL) {

            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto SetConfigurationCleanup;            
        }
                        
        RtlCopyMemory( Globals.PathBuffer, Value->Data, Value->DataLength );

        Globals.PathBuffer[Length / sizeof(WCHAR) - 1] = L'\0';

        //
        //  Add a trailing '\' if one is missing.
        //
        
        if (Globals.PathBuffer[Length/sizeof(WCHAR) - 3] != L'\\') {
           
            Globals.PathBuffer[Length/sizeof(WCHAR) - 2] = L'\\';

        }

        RtlInitUnicodeString(&Globals.MappingPath, Globals.PathBuffer);

    }

    //
    //  Ignore errors when looking for values in the registry.
    //  Default values will be used.
    //
    
    Status = STATUS_SUCCESS;
    
SetConfigurationCleanup:

    if (CloseHandle) {

        ZwClose( DriverRegKey );
    }

    return Status;

}


VOID
FreeGlobals(
    )
/*++

Routine Descrition:

    This routine cleans up the global buffers on both
    teardown and initialization failure.

Arguments:

Return Value:

    None.

--*/
{
    PAGED_CODE();

    Globals.FilterHandle = NULL;

    ExDeleteNPagedLookasideList( &Globals.QueueContextLookaside );

    if (Globals.PathBuffer != NULL) {
        
        ExFreePoolWithTag( Globals.PathBuffer, CSQ_STRING_TAG );
        Globals.PathBuffer = NULL;
    }

    RtlInitUnicodeString( &Globals.MappingPath, NULL );
}


NTSTATUS
Unload (
    _In_ FLT_FILTER_UNLOAD_FLAGS Flags
    )
/*++

Routine Description:

    This is the unload routine for this filter driver. This is called
    when the minifilter is about to be unloaded. We can fail this unload
    request if this is not a mandatory unloaded indicated by the Flags
    parameter.

Arguments:

    Flags - Indicating if this is a mandatory unload.

Return Value:

    Returns the final status of this operation.

--*/
{
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

    DebugTrace( CSQ_TRACE_LOAD_UNLOAD,
                ("[Csq]: CancelSafe!Unload\n") );

    FltUnregisterFilter( Globals.FilterHandle );

    FreeGlobals();
    
    return STATUS_SUCCESS;
}


//
//  Context cleanup routine.
//

VOID
ContextCleanup (
    _In_ PFLT_CONTEXT Context,
    _In_ FLT_CONTEXT_TYPE ContextType
    )
/*++

Routine Description:

    FltMgr calls this routine immediately before it deletes the context.

Arguments:

    Context - Pointer to the minifilter driver's portion of the context.

    ContextType - Type of context. Must be one of the following values:
        FLT_FILE_CONTEXT (Microsoft Windows Vista and later only.),
        FLT_INSTANCE_CONTEXT, FLT_STREAM_CONTEXT, FLT_STREAMHANDLE_CONTEXT,
        FLT_TRANSACTION_CONTEXT (Windows Vista and later only.), and
        FLT_VOLUME_CONTEXT

Return Value:

    None.

--*/
{
    UNREFERENCED_PARAMETER( Context );
    UNREFERENCED_PARAMETER( ContextType );

    PAGED_CODE();

    DebugTrace( CSQ_TRACE_CONTEXT_CALLBACK,
                ("[Csq]: CancelSafe!ContextCleanup\n") );
}

//
//  Instance setup/teardown routines.
//

NTSTATUS
InstanceSetup (
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
    _In_ DEVICE_TYPE VolumeDeviceType,
    _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
    )
/*++

Routine Description:

    This routine is called whenever a new instance is created on a volume. This
    gives us a chance to decide if we need to attach to this volume or not.

Arguments:

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

    Flags - Flags describing the reason for this attach request.

    VolumeDeviceType - Device type of the file system volume.
        Must be one of the following: FILE_DEVICE_CD_ROM_FILE_SYSTEM,
        FILE_DEVICE_DISK_FILE_SYSTEM, and FILE_DEVICE_NETWORK_FILE_SYSTEM.

    VolumeFilesystemType - File system type of the volume.

Return Value:

    STATUS_SUCCESS - attach
    STATUS_FLT_DO_NOT_ATTACH - do not attach

--*/
{
    PINSTANCE_CONTEXT InstCtx = NULL;
    NTSTATUS Status = STATUS_SUCCESS;

    UNREFERENCED_PARAMETER( Flags );
    UNREFERENCED_PARAMETER( VolumeDeviceType );
    UNREFERENCED_PARAMETER( VolumeFilesystemType );

    PAGED_CODE();

    DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK,
                ("[Csq]: CancelSafe!InstanceSetup\n") );

    //
    //  Allocate and initialize the instance context.
    //

    Status = FltAllocateContext( FltObjects->Filter,
                                 FLT_INSTANCE_CONTEXT,
                                 sizeof( INSTANCE_CONTEXT ),
                                 NonPagedPool,
                                 &InstCtx );

    if (!NT_SUCCESS( Status )) {

        DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to allocate instance context (Volume = %p, Instance = %p, Status = 0x%x)\n",
                    FltObjects->Volume,
                    FltObjects->Instance,
                    Status) );

        goto InstanceSetupCleanup;
    }

    Status = FltCbdqInitialize( FltObjects->Instance,
                                &InstCtx->Cbdq,
                                CsqInsertIo,
                                CsqRemoveIo,
                                CsqPeekNextIo,
                                CsqAcquire,
                                CsqRelease,
                                CsqCompleteCanceledIo );

    if (!NT_SUCCESS( Status )) {

        DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to initialize callback data queue (Volume = %p, Instance = %p, Status = 0x%x)\n",
                    FltObjects->Volume,
                    FltObjects->Instance,
                    Status) );

        goto InstanceSetupCleanup;
    }

    //
    //  Initialize the internal queue head and lock of the cancel safe queue.
    //

    InitializeListHead( &InstCtx->QueueHead );

    ExInitializeFastMutex( &InstCtx->Lock );

    //
    //  Initialize other members of the instance context.
    //

    InstCtx->Instance = FltObjects->Instance;

    InstCtx->WorkerThreadFlag = 0;

    KeInitializeEvent( &InstCtx->TeardownEvent, NotificationEvent, FALSE );

    //
    //  Set the instance context.
    //

    Status = FltSetInstanceContext( FltObjects->Instance,
                                    FLT_SET_CONTEXT_KEEP_IF_EXISTS,
                                    InstCtx,
                                    NULL );

    if (!NT_SUCCESS( Status )) {

        DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to set instance context (Volume = %p, Instance = %p, Status = 0x%x)\n",
                    FltObjects->Volume,
                    FltObjects->Instance,
                    Status) );

        goto InstanceSetupCleanup;
    }


InstanceSetupCleanup:

    if (InstCtx != NULL) {

        FltReleaseContext( InstCtx );
    }

    return Status;
}


NTSTATUS
InstanceQueryTeardown (
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
    )
/*++

Routine Description:

    This is called when an instance is being manually deleted by a
    call to FltDetachVolume or FilterDetach thereby giving us a
    chance to fail that detach request.

Arguments:

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

    Flags - Indicating where this detach request came from.

Return Value:

    Returns the status of this operation.

--*/
{
    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

    DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK,
                ("[Csq]: CancelSafe!InstanceQueryTeardown\n") );

    return STATUS_SUCCESS;
}


VOID
InstanceTeardownStart (
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
    )
/*++

Routine Description:

    This routine is called at the start of instance teardown.

Arguments:

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

    Flags - Reason why this instance is been deleted.

Return Value:

    None.

--*/
{
    PINSTANCE_CONTEXT InstCtx = 0;
    NTSTATUS Status;

    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

    DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK,
                ("[Csq]: CancelSafe!InstanceTeardownStart\n") );

    //
    //  Get a pointer to the instance context.
    //

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstCtx );

    if (!NT_SUCCESS( Status ))
    {
        FLT_ASSERT( !"Instance Context is missing" );
        return;
    }

    //
    //  Disable the insert to the cancel safe queue.
    //

    FltCbdqDisable( &InstCtx->Cbdq );

    //
    //  Remove all callback data from the queue and complete them.
    //

    PreReadEmptyQueueAndComplete( InstCtx );

    //
    //  Signal the worker thread if it is pended.
    //

    KeSetEvent( &InstCtx->TeardownEvent, 0, FALSE );

    //
    //  Cleanup
    //

    FltReleaseContext( InstCtx );
}


VOID
InstanceTeardownComplete (
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
    )
/*++

Routine Description:

    This routine is called at the end of instance teardown.

Arguments:

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

    Flags - Reason why this instance is been deleted.

Return Value:

    None.

--*/
{
    UNREFERENCED_PARAMETER( FltObjects );
    UNREFERENCED_PARAMETER( Flags );

    DebugTrace( CSQ_TRACE_INSTANCE_CALLBACK,
                ("[Csq]: CancelSafe!InstanceTeardownComplete\n") );

    PAGED_CODE();
}


//
//  Cbdq callback routines.
//

VOID
_IRQL_requires_max_(APC_LEVEL)
_IRQL_raises_(APC_LEVEL)
_Requires_lock_not_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
_Acquires_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
CsqAcquire(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _Out_ PKIRQL Irql
    )
/*++

Routine Description:

    FltMgr calls this routine to acquire the lock protecting the queue.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Irql - Returns the previous IRQL if a spinlock is acquired.  We do not use
           any spinlocks, so we ignore this.

Return Value:

    None.

--*/
{
    PINSTANCE_CONTEXT InstCtx;

    DebugTrace( CSQ_TRACE_CBDQ_CALLBACK,
                ("[Csq]: CancelSafe!CsqAcquire\n") );

    //
    //  Get a pointer to the instance context.
    //

    InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );

    //
    //  Acquire the lock.
    //

    ExAcquireFastMutex( &InstCtx->Lock );

    *Irql = 0;
}


VOID
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(APC_LEVEL)
_IRQL_raises_(PASSIVE_LEVEL)
_Requires_lock_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
_Releases_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
CsqRelease(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _In_ KIRQL Irql
    )
/*++

Routine Description:

    FltMgr calls this routine to release the lock protecting the queue.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Irql - Supplies the previous IRQL if a spinlock is acquired.  We do not use
           any spinlocks, so we ignore this.

Return Value:

    None.

--*/
{
    PINSTANCE_CONTEXT InstCtx;

    UNREFERENCED_PARAMETER( Irql );

    DebugTrace( CSQ_TRACE_CBDQ_CALLBACK,
                ("[Csq]: CancelSafe!CsqRelease\n") );

    //
    //  Get a pointer to the instance context.
    //

    InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );

    //
    //  Release the lock.
    //

    ExReleaseFastMutex( &InstCtx->Lock );
}


NTSTATUS
CsqInsertIo(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _In_ PFLT_CALLBACK_DATA Data,
    _In_opt_ PVOID Context
    )
/*++

Routine Description:

    FltMgr calls this routine to insert an entry into our pending I/O queue.
    The queue is already locked before this routine is called.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Data - Supplies the callback data for the operation that is being
           inserted into the queue.

    Context - Supplies user-defined context information.

Return Value:

    STATUS_SUCCESS if the function completes successfully.  Otherwise a valid
    NTSTATUS code is returned.

--*/
{
    PINSTANCE_CONTEXT InstCtx;
    PFLT_GENERIC_WORKITEM WorkItem = NULL;
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN WasQueueEmpty;

    UNREFERENCED_PARAMETER( Context );

    DebugTrace( CSQ_TRACE_CBDQ_CALLBACK,
                ("[Csq]: CancelSafe!CsqInsertIo\n") );

    //
    //  Get a pointer to the instance context.
    //

    InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );

    //
    //  Save the queue state before inserting to it.
    //

    WasQueueEmpty = IsListEmpty( &InstCtx->QueueHead );

    //
    //  Insert the callback data entry into the queue.
    //

    InsertTailList( &InstCtx->QueueHead,
                    &Data->QueueLinks );

    //
    //  Queue a work item if no worker thread present.
    //

    if (WasQueueEmpty &&
        InterlockedIncrement( &InstCtx->WorkerThreadFlag ) == 1) {

        WorkItem = FltAllocateGenericWorkItem();

        if (WorkItem) {

            Status = FltQueueGenericWorkItem( WorkItem,
                                              InstCtx->Instance,
                                              PreReadWorkItemRoutine,
                                              DelayedWorkQueue,
                                              InstCtx->Instance );

            if (!NT_SUCCESS( Status )) {

                DebugTrace( CSQ_TRACE_CBDQ_CALLBACK | CSQ_TRACE_ERROR,
                            ("[Csq]: Failed to queue the work item (Status = 0x%x)\n",
                            Status) );

                FltFreeGenericWorkItem( WorkItem );
            }

        } else {

            Status = STATUS_INSUFFICIENT_RESOURCES;
        }

        if (!NT_SUCCESS( Status )) {

            //
            //  Remove the callback data that was inserted into the queue.
            //

            RemoveTailList( &InstCtx->QueueHead );
        }
    }

    return Status;
}


VOID
CsqRemoveIo(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _In_ PFLT_CALLBACK_DATA Data
    )
/*++

Routine Description:

    FltMgr calls this routine to remove an entry from our pending I/O queue.
    The queue is already locked before this routine is called.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Data - Supplies the callback data that is to be removed.

Return Value:

    None.

--*/
{
    UNREFERENCED_PARAMETER( DataQueue );

    DebugTrace( CSQ_TRACE_CBDQ_CALLBACK,
                ("[Csq]: CancelSafe!CsqRemoveIo\n") );

    //
    //  Remove the callback data entry from the queue.
    //

    RemoveEntryList( &Data->QueueLinks );
}


PFLT_CALLBACK_DATA
CsqPeekNextIo(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _In_opt_ PFLT_CALLBACK_DATA Data,
    _In_opt_ PVOID PeekContext
    )
/*++

Routine Description:

    FltMgr calls this routine to look for an entry on our pending I/O queue.
    The queue is already locked before this routine is called.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Data - Supplies the callback data we should start our search from.
           If this is NULL, we start at the beginning of the list.

    PeekContext - Supplies user-defined context information.

Return Value:

    A pointer to the next callback data structure, or NULL.

--*/
{
    PINSTANCE_CONTEXT InstCtx;
    PLIST_ENTRY NextEntry;
    PFLT_CALLBACK_DATA NextData;

    UNREFERENCED_PARAMETER( PeekContext );

    DebugTrace( CSQ_TRACE_CBDQ_CALLBACK,
                ("[Csq]: CancelSafe!CsqPeekNextIo\n") );

    //
    //  Get a pointer to the instance context.
    //

    InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );

    //
    //  If the supplied callback "Data" is NULL, the "NextIo" is the first entry
    //  in the queue; or it is the next list entry in the queue.
    //

    if (Data == NULL) {

        NextEntry = InstCtx->QueueHead.Flink;

    } else {

        NextEntry =  Data->QueueLinks.Flink;
    }

    //
    //  Return NULL if we hit the end of the queue or the queue is empty.
    //

    if (NextEntry == &InstCtx->QueueHead) {

        return NULL;
    }

    NextData = CONTAINING_RECORD( NextEntry, FLT_CALLBACK_DATA, QueueLinks );

    return NextData;
}


VOID
CsqCompleteCanceledIo(
    _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
    _Inout_ PFLT_CALLBACK_DATA Data
    )
/*++

Routine Description:

    FltMgr calls this routine to complete an operation as cancelled that was
    previously pended. The queue is already locked before this routine is called.

Arguments:

    DataQueue - Supplies a pointer to the queue itself.

    Data - Supplies the callback data that is to be canceled.

Return Value:

    None.

--*/
{
    PQUEUE_CONTEXT QueueCtx;

    UNREFERENCED_PARAMETER( DataQueue );

    DebugTrace( CSQ_TRACE_CBDQ_CALLBACK,
                ("[Csq]: CancelSafe!CsqCompleteCanceledIo\n") );

    QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];

    //
    //  Just complete the operation as canceled.
    //

    Data->IoStatus.Status = STATUS_CANCELLED;
    Data->IoStatus.Information = 0;

    FltCompletePendedPreOperation( Data,
                                   FLT_PREOP_COMPLETE,
                                   0 );

    //
    //  Free the extra storage that was allocated for this canceled I/O.
    //

    ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside,
                                 QueueCtx );
}


FLT_PREOP_CALLBACK_STATUS
PreRead (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
/*++

Routine Description:

    Handle pre-read.

Arguments:

    Data - Pointer to the filter callbackData that is passed to us.

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

    CompletionContext - The context for the completion routine for this
        operation.

Return Value:

    The return value is the status of the operation.

--*/
{

    PINSTANCE_CONTEXT InstCtx = NULL;
    PQUEUE_CONTEXT QueueCtx = NULL;
    PFLT_FILE_NAME_INFORMATION NameInfo = NULL;
    NTSTATUS CbStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
    NTSTATUS Status;

    UNREFERENCED_PARAMETER( CompletionContext );

    DebugTrace( CSQ_TRACE_PRE_READ,
                ("[Csq]: CancelSafe!PreRead\n") );

    //
    //  Skip IRP_PAGING_IO, IRP_SYNCHRONOUS_PAGING_IO and
    //  TopLevelIrp.
    //

    if ((Data->Iopb->IrpFlags & IRP_PAGING_IO) ||
        (Data->Iopb->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) ||
        IoGetTopLevelIrp()) {

        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    //
    //  Get and parse the file name
    //

    Status = FltGetFileNameInformation( Data,
                                        FLT_FILE_NAME_NORMALIZED
                                          | FLT_FILE_NAME_QUERY_DEFAULT,
                                        &NameInfo );

    if (!NT_SUCCESS( Status )) {

        DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to get filename (Status = 0x%x)\n",
                    Status) );

        goto PreReadCleanup;
    }

    Status = FltParseFileNameInformation( NameInfo );

    if (!NT_SUCCESS( Status )) {

        DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to parse filename (Name = %wZ, Status = 0x%x)\n",
                    &NameInfo->Name,
                    Status) );

        goto PreReadCleanup;
    }

    //
    //  Compare to see if this file I/O is to be pended.
    //

    if (!RtlPrefixUnicodeString( &Globals.MappingPath, &NameInfo->ParentDir, TRUE )) {

        goto PreReadCleanup;                
    }

    //
    //  Since Fast I/O operations cannot be queued, we could return 
    //  FLT_PREOP_SUCCESS_NO_CALLBACK at this point. In this sample,
    //  we disallow Fast I/O for this magic file in order to force an IRP
    //  to be sent to us again. The purpose of doing that is to demonstrate
    //  the cancel safe queue, which may not be true in the real world.
    //

    if (!FLT_IS_IRP_OPERATION( Data )) {

        CbStatus = FLT_PREOP_DISALLOW_FASTIO;
        goto PreReadCleanup;
    }

    //
    //  Allocate a context for each I/O to be inserted into the queue.
    //

    QueueCtx = ExAllocateFromNPagedLookasideList( &Globals.QueueContextLookaside );

    if (QueueCtx == NULL) {

        DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to allocate from NPagedLookasideList (Status = 0x%x)\n",
                    Status) );

        goto PreReadCleanup;
    }

    RtlZeroMemory(QueueCtx, sizeof(QUEUE_CONTEXT));

    //
    //  Get the instance context.
    //

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstCtx );

    if (!NT_SUCCESS( Status )) {

        FLT_ASSERT( !"Instance context is missing" );
        goto PreReadCleanup;
    }

    //
    //  Set the queue context
    //

    Data->QueueContext[0] = (PVOID) QueueCtx;
    Data->QueueContext[1] = NULL;

    //
    //  Insert the callback data into the cancel safe queue
    //

    Status = FltCbdqInsertIo( &InstCtx->Cbdq,
                              Data,
                              &QueueCtx->CbdqIoContext,
                              0 );

    if (Status == STATUS_SUCCESS) {

        //
        //  In general, we can create a worker thread here as long as we can
        //  correctly handle the insert/remove race conditions b/w multi threads.
        //  In this sample, the worker thread creation is done in CsqInsertIo.
        //  This is a simpler solution because CsqInsertIo is atomic with 
        //  respect to other CsqXxxIo callback routines.
        //

        CbStatus = FLT_PREOP_PENDING;

    } else {

        DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
                    ("[Csq]: Failed to insert into cbdq (Status = 0x%x)\n",
                    Status) );
    }

PreReadCleanup:

    //
    //  Clean up
    //

    if (QueueCtx && CbStatus != FLT_PREOP_PENDING) {

        ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside, QueueCtx );
    }

    if (NameInfo) {

        FltReleaseFileNameInformation( NameInfo );
    }

    if (InstCtx) {

        FltReleaseContext( InstCtx );
    }

    return CbStatus;
}


VOID
PreReadWorkItemRoutine(
    _In_ PFLT_GENERIC_WORKITEM WorkItem,
    _In_ PFLT_FILTER Filter,
    _In_ PVOID Context
    )
/*++

Routine Description:

    This WorkItem routine is called in the system thread context to process
    all the pended I/O in this mini filter's cancel safe queue. For each I/O
    in the queue, it completes the I/O after pending the operation for a
    period of time. The thread exits when the queue is empty.

Arguments:

    WorkItem - Unused.

    Filter - Unused.

    Context - Context information.

Return Value:

    None.

--*/
{
    PINSTANCE_CONTEXT InstCtx = NULL;
    PFLT_CALLBACK_DATA Data;
    PFLT_INSTANCE Instance = (PFLT_INSTANCE)Context;
    PQUEUE_CONTEXT QueueCtx;
    NTSTATUS Status;
    FLT_PREOP_CALLBACK_STATUS callbackStatus;

    UNREFERENCED_PARAMETER( WorkItem );
    UNREFERENCED_PARAMETER( Filter );

    DebugTrace( CSQ_TRACE_PRE_READ,
                ("[Csq]: CancelSafe!PreReadWorkItemRoutine\n") );

    //
    //  Get a pointer to the instance context.
    //

    Status = FltGetInstanceContext( Instance,
                                    &InstCtx );

    if (!NT_SUCCESS( Status ))
    {
        FLT_ASSERT( !"Instance Context is missing" );
        return;
    }

    //
    //  Process all the pended I/O in the cancel safe queue
    //

    for (;;) {

        callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;

        PreReadPendIo( InstCtx );

        //
        //  WorkerThreadFlag >= 1;
        //  Here we reduce it to 1.
        //

        InterlockedExchange( &InstCtx->WorkerThreadFlag, 1 );

        //
        //  Remove an I/O from the cancel safe queue.
        //

        Data = FltCbdqRemoveNextIo( &InstCtx->Cbdq,
                                    NULL);

        if (Data) {

            QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];

            PreReadProcessIo( Data );

            //
            //  Check to see if we need to lock the user buffer.
            //
            //  If the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is set we don't 
            //  have to lock the buffer because its already a system buffer.
            //
            //  If the MdlAddress is NULL and the buffer is a user buffer, 
            //  then we have to construct one in order to look at the buffer.
            //
            //  If the length of the buffer is zero there is nothing to read,
            //  so we cannot construct a MDL.
            //

            if (!FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) && 
                Data->Iopb->Parameters.Read.MdlAddress == NULL &&
                Data->Iopb->Parameters.Read.Length > 0) {

                Status = FltLockUserBuffer( Data );

                if (!NT_SUCCESS( Status )) {
                    
                    //
                    //  If could not lock the user buffer we cannot
                    //  allow the IO to go below us. Because we are 
                    //  in a different VA space and the buffer is a
                    //  user mode address, we will either fault or 
                    //  corrpt data
                    //
                   
                    DebugTrace( CSQ_TRACE_PRE_READ | CSQ_TRACE_ERROR,
                                ("[Csq]: Failed to lock user buffer (Status = 0x%x)\n",
                                Status) );

                    callbackStatus = FLT_PREOP_COMPLETE;
                    Data->IoStatus.Status = Status;
                }
            }

            //
            //  Complete the I/O
            //

            FltCompletePendedPreOperation( Data,
                                           callbackStatus,
                                           NULL );

            //
            //  Free the extra storage that was allocated for this I/O.
            //

            ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside,
                                         QueueCtx );

        } else {

            //
            //  At this moment it is possible that a new IO is being inserted
            //  into the queue in the CsqInsertIo routine. Now that the queue is
            //  empty, CsqInsertIo needs to make a decision on whether to create
            //  a new worker thread. The decision is based on the race between
            //  the InterlockedIncrement in CsqInsertIo and the
            //  InterlockedDecrement as below. There are two situations:
            //
            //  (1) If the decrement executes earlier before the increment,
            //      the flag will be decremented to 0 so this worker thread
            //      will return. Then CsqInsertIo will increment the flag
            //      from 0 to 1, and therefore create a new worker thread.
            //  (2) If the increment executes earlier before the decrement,
            //      the flag will be first incremented to 2 in CsqInsertIo
            //      so a new worker thread will not be satisfied. Then the
            //      decrement as below will lower the flag down to 1, and
            //      therefore continue this worker thread.
            //

            if (InterlockedDecrement( &InstCtx->WorkerThreadFlag ) == 0) {
                
                break;
            }
            
        }
    }

    //
    //  Clean up
    //

    FltReleaseContext(InstCtx);

    FltFreeGenericWorkItem(WorkItem);
}


NTSTATUS
PreReadPendIo(
    _In_ PINSTANCE_CONTEXT InstanceContext
    )
/*++

Routine Description:

    This routine waits for a period of time or until the instance is
    torndown.

Arguments:

    InstanceContext - Supplies a pointer to the instance context.

Return Value:

    The return value is the status of the operation.

--*/
{
    LARGE_INTEGER DueTime;
    NTSTATUS Status;

    //
    //  Delay or get signaled if the instance is torndown.
    //

    DueTime.QuadPart = (LONGLONG) - Globals.TimeDelay;

    Status = KeWaitForSingleObject( &InstanceContext->TeardownEvent,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    &DueTime );

    return Status;
}


NTSTATUS
PreReadProcessIo(
    _Inout_ PFLT_CALLBACK_DATA Data
    )
/*++

Routine Description:

    This routine process the I/O that was removed from the queue.

Arguments:

    Data - Supplies the callback data that was removed from the queue.

Return Value:

    The return value is the status of the operation.

--*/
{
    UNREFERENCED_PARAMETER( Data );

    return STATUS_SUCCESS;
}


VOID
PreReadEmptyQueueAndComplete(
    _In_ PINSTANCE_CONTEXT InstanceContext
    )
/*++

Routine Description:

    This routine empties the cancel safe queue and complete all the
    pended pre-read operations.

Arguments:

    InstanceContext - Supplies a pointer to the instance context.

Return Value:

    None.

--*/
{
    NTSTATUS Status;
    FLT_PREOP_CALLBACK_STATUS callbackStatus;
    PFLT_CALLBACK_DATA Data;
    PQUEUE_CONTEXT QueueCtx;

    do {

        callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;

        Data = FltCbdqRemoveNextIo( &InstanceContext->Cbdq,
                                    NULL );

        if (Data) {

            QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];

            //
            //  Check to see if we need to lock the user buffer.
            //
            //  If the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is set we don't 
            //  have to lock the buffer because its already a system buffer.
            //
            //  If the MdlAddress is NULL and the buffer is a user buffer, 
            //  then we have to construct one in order to look at the buffer.
            //
            //  If the length of the buffer is zero there is nothing to read,
            //  so we cannot construct a MDL.
            //

            if (!FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) && 
                Data->Iopb->Parameters.Read.MdlAddress == NULL &&
                Data->Iopb->Parameters.Read.Length > 0) {

                Status = FltLockUserBuffer( Data );

                if (!NT_SUCCESS( Status )) {

                    //
                    //  If could not lock the user buffer we cannot
                    //  allow the IO to go below us. Because we are 
                    //  in a different VA space and the buffer is a
                    //  user mode address, we will either fault or 
                    //  corrpt data
                    //
                   
                    callbackStatus = FLT_PREOP_COMPLETE;
                    Data->IoStatus.Status = Status;
                }
            }

            FltCompletePendedPreOperation( Data,
                                           callbackStatus,
                                           NULL );

            ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside,
                                         QueueCtx );
        }

    } while (Data);
}

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS