diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /filesys/miniFilter | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'filesys/miniFilter')
147 files changed, 49214 insertions, 0 deletions
diff --git a/filesys/miniFilter/MetadataManager/DataStore.c b/filesys/miniFilter/MetadataManager/DataStore.c new file mode 100644 index 00000000..736e5088 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/DataStore.c @@ -0,0 +1,1082 @@ +/*++ + +Copyright (c) 2002 - 2003 Microsoft Corporation + +Module Name: + + datastore.c + +Abstract: + + This module contains routines that provide support for storage and + retrieval of the filter metadata manager filter metadata. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FmmOpenMetadata) +#pragma alloc_text(PAGE, FmmCloseMetadata) +#pragma alloc_text(PAGE, FmmReleaseMetadataFileReferences) +#pragma alloc_text(PAGE, FmmReacquireMetadataFileReferences) +#pragma alloc_text(PAGE, FmmSetMetadataOpenTriggerFileObject) +#pragma alloc_text(PAGE, FmmBeginFileSystemOperation) +#pragma alloc_text(PAGE, FmmEndFileSystemOperation) +#endif + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_IRQL_requires_max_(PASSIVE_LEVEL) +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, + ("[Fmm]: Opening metadata file ... (Volume = %p, 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; +} + + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_IRQL_requires_max_(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(); + + FLT_ASSERT( InstanceContext->MetadataHandle ); + FLT_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->MetadataResource ); + + 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->MetadataResource ); + + +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->MetadataResource ); + + 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->MetadataResource ); + + +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->MetadataResource ); + + 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 + // + + FLT_ASSERT((instanceContext->MetadataOpenTriggerFileObject == NULL) || + (instanceContext->MetadataOpenTriggerFileObject == Cbd->Iopb->TargetFileObject)); + + instanceContext->MetadataOpenTriggerFileObject = Cbd->Iopb->TargetFileObject; + } + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + +FmmSetMetadataOpenTriggerFileObjectCleanup: + + // + // Release the references we have acquired + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + } + + + return status;; +} + +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_Releases_lock_(InstanceContext->MetadataResource) +_IRQL_requires_max_(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 + // + + FLT_ASSERT( !FlagOn( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION ) ); + + SetFlag( InstanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION ); + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &InstanceContext->MetadataResource ); + +} + + +_Acquires_lock_(_Global_critical_region_) +_Requires_lock_not_held_(InstanceContext->MetadataResource) +_Acquires_exclusive_lock_(InstanceContext->MetadataResource) +_IRQL_requires_max_(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->MetadataResource ); + + + // + // 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 + // + + FLT_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->MetadataResource ); + + 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 + // + + FLT_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->MetadataResource ); + + +FmmIsMetadataOpenCleanup: + + // + // Release the references we have acquired + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + } + + + return status; +} + + +#endif + + diff --git a/filesys/miniFilter/MetadataManager/MetadataManager.rc b/filesys/miniFilter/MetadataManager/MetadataManager.rc new file mode 100644 index 00000000..850a65e4 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManager.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Metadata Management File System Filter Driver Sample" +#define VER_INTERNALNAME_STR "FMM.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/MetadataManager/MetadataManager.sln b/filesys/miniFilter/MetadataManager/MetadataManager.sln new file mode 100644 index 00000000..9af2fc43 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManager.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmm", "fmm.vcxproj", "{A95B7D4F-B926-4E1F-A051-E66091E08D3A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|Win32.ActiveCfg = Debug|Win32 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|Win32.Build.0 = Debug|Win32 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|Win32.ActiveCfg = Release|Win32 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|Win32.Build.0 = Release|Win32 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|x64.ActiveCfg = Debug|x64 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Debug|x64.Build.0 = Debug|x64 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|x64.ActiveCfg = Release|x64 + {A95B7D4F-B926-4E1F-A051-E66091E08D3A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/MetadataManager/MetadataManagerInit.c b/filesys/miniFilter/MetadataManager/MetadataManagerInit.c new file mode 100644 index 00000000..222159f7 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManagerInit.c @@ -0,0 +1,865 @@ +/*++ + +Copyright (c) 1999 - 2003 Microsoft Corporation + +Module Name: + + MetadataManagerInit.c + +Abstract: + + This is the main module of the kernel mode filter driver implementing + filter metadata management. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + +// +// Global variables +// + +FMM_GLOBAL_DATA Globals; + + +// +// Local constants +// + +#define FMM_UNSUPPORTED_DEVICE_CHARACS FILE_FLOPPY_DISKETTE | \ + FILE_READ_ONLY_DEVICE | \ + FILE_VIRTUAL_VOLUME + +// +// Local function prototypes +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +FmmUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +VOID +FmmContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +FmmInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +FmmInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +FmmInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +FmmInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +#if DBG + +VOID +FmmInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ); + +#endif + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) + +#if DBG +#pragma alloc_text(INIT, FmmInitializeDebugLevel) +#endif + +#pragma alloc_text(PAGE, FmmUnload) +#pragma alloc_text(PAGE, FmmContextCleanup) +#pragma alloc_text(PAGE, FmmInstanceSetup) +#pragma alloc_text(PAGE, FmmInstanceQueryTeardown) +#pragma alloc_text(PAGE, FmmInstanceTeardownStart) +#pragma alloc_text(PAGE, FmmInstanceTeardownComplete) +#endif + + +// +// If we need to verify that the metadata file is indeed open whenever +// a create suceeds on the volume, then we need to monitor all creates +// not just DASD creates. + +// If that is not the case, then we are better off telling filter manager +// to show us only DASD creates. That way we can avoid the performance +// penalty of being called for all creates when we only have use for DASD +// creates. +// + + +#if VERIFY_METADATA_OPENED + +#define OPERATION_REGISTRATION_FLAGS_FOR_CREATE (FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO) + +#else + +#define OPERATION_REGISTRATION_FLAGS_FOR_CREATE (FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO | FLTFL_OPERATION_REGISTRATION_SKIP_NON_DASD_IO) + +#endif + + + +// +// Filters callback routines +// + +FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + OPERATION_REGISTRATION_FLAGS_FOR_CREATE, + FmmPreCreate, + FmmPostCreate }, + + { IRP_MJ_CLEANUP, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO | FLTFL_OPERATION_REGISTRATION_SKIP_NON_DASD_IO, + FmmPreCleanup, + FmmPostCleanup }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO | FLTFL_OPERATION_REGISTRATION_SKIP_NON_DASD_IO, + FmmPreFSControl, + FmmPostFSControl }, + + { IRP_MJ_DEVICE_CONTROL, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + FmmPreDeviceControl, + FmmPostDeviceControl }, + + { IRP_MJ_SHUTDOWN, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + FmmPreShutdown, + NULL }, + + { IRP_MJ_PNP, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + FmmPrePnp, + FmmPostPnp }, + + { IRP_MJ_OPERATION_END } +}; + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_INSTANCE_CONTEXT, + 0, + FmmContextCleanup, + FMM_INSTANCE_CONTEXT_SIZE, + FMM_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 + FmmUnload, // Filters unload routine + FmmInstanceSetup, // InstanceSetup routine + FmmInstanceQueryTeardown, // InstanceQueryTeardown routine + FmmInstanceTeardownStart, // InstanceTeardownStart routine + FmmInstanceTeardownComplete, // 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 ); + + + RtlZeroMemory( &Globals, sizeof( Globals ) ); + +#if DBG + + // + // Initialize global debug level + // + + FmmInitializeDebugLevel( RegistryPath ); + +#else + + UNREFERENCED_PARAMETER( RegistryPath ); + +#endif + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Fmm]: Driver being loaded\n") ); + + + + // + // Register with the filter manager + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Start filtering I/O + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( Globals.Filter ); + } + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Fmm]: Driver loaded complete (Status = 0x%08X)\n", + status) ); + + return status; +} + +#if DBG + +VOID +FmmInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the filter DebugLevel parameter from + the registry. This value will be found in the registry location + indicated by the RegistryPath passed in. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + Globals.DebugLevel = DEBUG_TRACE_ERROR; + + // + // Open the desired registry key + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (NT_SUCCESS( status )) { + + // + // Read the DebugFlags value from the registry. + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *((PULONG) &(((PKEY_VALUE_PARTIAL_INFORMATION) buffer)->Data)); + } + + // + // Close the registry entry + // + + ZwClose( driverRegKey ); + + } + +} + +#endif + +NTSTATUS +FmmUnload ( + _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( DEBUG_TRACE_LOAD_UNLOAD, + ("[Fmm]: Unloading driver\n") ); + + + FltUnregisterFilter( Globals.Filter ); + Globals.Filter = NULL; + + return STATUS_SUCCESS; +} + +VOID +FmmContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +{ + PFMM_INSTANCE_CONTEXT instanceContext; + + PAGED_CODE(); + + switch(ContextType) { + + case FLT_INSTANCE_CONTEXT: + + instanceContext = Context; + + DebugTrace( DEBUG_TRACE_INFO, + ("[Fmm]: Cleaning up instance context for volume (Context = %p)\n", + instanceContext) ); + + ExDeleteResourceLite( &instanceContext->MetadataResource ); + + break; + + } + + DebugTrace( DEBUG_TRACE_INFO, + ("[Fmm]: Context cleanup complete.\n") ); + +} + +// +// Instance setup/teardown routines. +// + +NTSTATUS +FmmInstanceSetup ( + _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. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + PFMM_INSTANCE_CONTEXT instanceContext = NULL; + PDEVICE_OBJECT diskDeviceObject; + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( VolumeDeviceType ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance setup started (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + // + // Check if the file system mounted is ntfs or fat + // + // The sample picks NTFS, FAT and ReFS as examples. The metadata + // handling demostrated in the sample can be applied + // to any file system + // + + if (VolumeFilesystemType != FLT_FSTYPE_NTFS && VolumeFilesystemType != FLT_FSTYPE_FAT && VolumeFilesystemType != FLT_FSTYPE_REFS) { + + // + // An unknown file system is mounted which we do not care + // + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Unsupported file system mounted (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + status = STATUS_NOT_SUPPORTED; + goto FmmInstanceSetupCleanup; + } + + // + // Get the disk device object and make sure it is a disk device type and does not + // have any of the device characteristics we do not support. + // + // The sample picks the device characteristics to demonstrate how to access and + // check the device characteristics in order to make a decision to attach. The + // metadata handling demostrated in the sample is not limited to the + // characteristics we have used in the sample. + // + + status = FltGetDiskDeviceObject( FltObjects->Volume, &diskDeviceObject ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to get device object (Volume = %p, Status = 0x%08X)\n", + FltObjects->Volume, + status) ); + goto FmmInstanceSetupCleanup; + } + + if (diskDeviceObject->DeviceType != FILE_DEVICE_DISK || + FlagOn( diskDeviceObject->Characteristics, FMM_UNSUPPORTED_DEVICE_CHARACS )) { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Unsupported device type or device characteristics (Volume = %p, Instance = %p DiskDeviceObjectDeviceTYpe = 0x%x, DiskDeviceObjectCharacteristics = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + diskDeviceObject->DeviceType, + diskDeviceObject->Characteristics) ); + + ObDereferenceObject( diskDeviceObject ); + status = STATUS_NOT_SUPPORTED; + goto FmmInstanceSetupCleanup; + } + + ObDereferenceObject( diskDeviceObject ); + + // + // Allocate and initialize the context for this volume + // + + status = FltAllocateContext( FltObjects->Filter, + FLT_INSTANCE_CONTEXT, + FMM_INSTANCE_CONTEXT_SIZE, + NonPagedPool, + &instanceContext ); + + if( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to allocate instance context (Volume = %p, Instance = %p, Status = 0x%08X)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto FmmInstanceSetupCleanup; + } + + FLT_ASSERT( instanceContext != NULL ); + + RtlZeroMemory( instanceContext, FMM_INSTANCE_CONTEXT_SIZE ); + + instanceContext->Flags = 0; + instanceContext->Instance = FltObjects->Instance; + instanceContext->FilesystemType = VolumeFilesystemType; + instanceContext->Volume = FltObjects->Volume; + ExInitializeResourceLite( &instanceContext->MetadataResource ); + + + // + // Set the instance context. + // + + status = FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + instanceContext, + NULL ); + + if( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, + ("[Fmm]: Failed to set instance context (Volume = %p, Instance = %p, Status = 0x%08X)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + goto FmmInstanceSetupCleanup; + } + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); + + // + // Sanity - the instance context cannot be in a transition state during instance setup + // + + FLT_ASSERT( !FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION) ); + + // + // Open the filter metadata on disk + // + // The sample will attach to volume if it finds its metadata file on the volume. + // If this is a manual attachment then the sample filter will create its metadata + // file and attach to the volume. + // + + status = FmmOpenMetadata( instanceContext, + BooleanFlagOn( Flags, FLTFL_INSTANCE_SETUP_MANUAL_ATTACHMENT ) ); + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + if (!NT_SUCCESS( status )) { + + goto FmmInstanceSetupCleanup; + } + + +FmmInstanceSetupCleanup: + + // + // If FltAllocateContext suceeded then we MUST release the context, + // irrespective of whether FltSetInstanceContext suceeded or not. + // + // FltAllocateContext increments the ref count by one. + // A successful FltSetInstanceContext increments the ref count by one + // and also associates the context with the file system object + // + // FltReleaseContext decrements the ref count by one. + // + // When FltSetInstanceContext succeeds, calling FltReleaseContext will + // leave the context with a ref count of 1 corresponding to the internal + // reference to the context from the file system structures + // + // When FltSetInstanceContext fails, calling FltReleaseContext will + // leave the context with a ref count of 0 which is correct since + // there is no reference to the context from the file system structures + // + + if ( instanceContext != NULL ) { + + FltReleaseContext( instanceContext ); + } + + + if (NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance setup complete (Volume = %p, Instance = %p). Filter will attach to the volume.\n", + FltObjects->Volume, + FltObjects->Instance) ); + } else { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance setup complete (Volume = %p, Instance = %p). Filter will not attach to the volume.\n", + FltObjects->Volume, + FltObjects->Instance) ); + } + + // + // If this is an automatic attachment (mount, load, etc) and we are not + // attaching to this volume because we do not support attaching to this + // volume, then simply return STATUS_FLT_DO_NOT_ATTACH. If we return + // anything else fltmgr logs an event log indicating failure to attach. + // Since this failure to attach is not really an error, we do not want + // this failure to be logged as an error in the event log. For all other + // error codes besides the ones we consider "normal", if is ok for fltmgr + // to actually log the failure to attach. + // + // If this is a manual attach attempt that we have failed then we want to + // give the user a clear indication of why the attachment failed. Hence in + // this case, we will not override the error status with STATUS_FLT_DO_NOT_ATTACH + // irrespective of the cause of the failure to attach + // + + if (status == STATUS_NOT_SUPPORTED && + !FlagOn( Flags, FLTFL_INSTANCE_SETUP_MANUAL_ATTACHMENT )) { + + status = STATUS_FLT_DO_NOT_ATTACH; + } + + return status; +} + + +NTSTATUS +FmmInstanceQueryTeardown ( + _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( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance query teardown started (Instance = %p)\n", + FltObjects->Instance) ); + + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance query teadown ended (Instance = %p)\n", + FltObjects->Instance) ); + return STATUS_SUCCESS; +} + + +VOID +FmmInstanceTeardownStart ( + _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. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance teardown start started (Instance = %p)\n", + FltObjects->Instance) ); + + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance teardown start ended (Instance = %p)\n", + FltObjects->Instance) ); +} + + +VOID +FmmInstanceTeardownComplete ( + _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. + +--*/ +{ + PFMM_INSTANCE_CONTEXT instanceContext; + NTSTATUS status; + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance teardown complete started (Instance = %p)\n", + FltObjects->Instance) ); + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + // + // Acquire exclusive access to the instance context + // + + FmmAcquireResourceExclusive( &instanceContext->MetadataResource ); + + // + // Sanity - the instance context cannot be in a transition state during instance teardown complete + // + + FLT_ASSERT( !FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_TRANSITION) ); + + + if (FlagOn( instanceContext->Flags, INSTANCE_CONTEXT_F_METADATA_OPENED )) { + + // + // Close the metadata file + // + + FmmCloseMetadata( instanceContext ); + } + + + // + // Relinquish exclusive access to the instance context + // + + FmmReleaseResource( &instanceContext->MetadataResource ); + + FltReleaseContext( instanceContext ); + } + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Fmm]: Instance teardown complete ended (Instance = %p)\n", + FltObjects->Instance) ); +} + diff --git a/filesys/miniFilter/MetadataManager/MetadataManagerProc.h b/filesys/miniFilter/MetadataManager/MetadataManagerProc.h new file mode 100644 index 00000000..bee71cd7 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManagerProc.h @@ -0,0 +1,255 @@ +/*++ + +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, + _Flt_CompletionContext_Outptr_ 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, + _Flt_CompletionContext_Outptr_ 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, + _Flt_CompletionContext_Outptr_ 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, + _Flt_CompletionContext_Outptr_ 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, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +FmmPrePnp ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ 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 +// + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +FmmOpenMetadata ( + _In_ PFMM_INSTANCE_CONTEXT InstanceContext, + _In_ BOOLEAN CreateIfNotPresent + ); + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_IRQL_requires_max_(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 + ); + +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(InstanceContext->MetadataResource) +_Releases_lock_(InstanceContext->MetadataResource) +_IRQL_requires_max_(APC_LEVEL) +VOID +FmmBeginFileSystemOperation ( + IN PFMM_INSTANCE_CONTEXT InstanceContext + ); + +_Acquires_lock_(_Global_critical_region_) +_Requires_lock_not_held_(InstanceContext->MetadataResource) +_Acquires_exclusive_lock_(InstanceContext->MetadataResource) +_IRQL_requires_max_(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 +// + +_Acquires_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +FORCEINLINE +VOID +FmmAcquireResourceExclusive ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_exclusive_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + !ExIsResourceAcquiredSharedLite(Resource)); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); +} + +_Acquires_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +FORCEINLINE +VOID +FmmAcquireResourceShared ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_shared_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceSharedLite( Resource, TRUE ); +} + +_Releases_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +FORCEINLINE +VOID +FmmReleaseResource ( + _Inout_ _Requires_lock_held_(*_Curr_) _Releases_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + ExIsResourceAcquiredSharedLite(Resource)); + + ExReleaseResourceLite(Resource); + KeLeaveCriticalRegion(); +} + + + diff --git a/filesys/miniFilter/MetadataManager/MetadataManagerStruc.h b/filesys/miniFilter/MetadataManager/MetadataManagerStruc.h new file mode 100644 index 00000000..edce9c75 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/MetadataManagerStruc.h @@ -0,0 +1,197 @@ +/*++ + +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 MetadataResource; + + // + // 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 + diff --git a/filesys/miniFilter/MetadataManager/ReadMe.md b/filesys/miniFilter/MetadataManager/ReadMe.md new file mode 100644 index 00000000..52230ee1 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/ReadMe.md @@ -0,0 +1,21 @@ +Metadata Manager File System Minifilter Driver +============================================== + +The Metadata Manager minifilter sample serves as an example if you want to use files for storing metadata that corresponds to your minifilters. The implementation of this sample depicts scenarios in which modifications to the file might have to be blocked or the minifilter might be required to close the file temporarily. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The Metadata Manager minifilter opens a file when it is first loaded. After that, the minifilter monitors open, close, file control, device control, and Plug and Play (PnP) operations to identify scenarios in which it should close its metadata file or block all writes to it. Applications such as chkdsk obtain implicit or explicit exclusive locks on the volume, and the metadata minifilter demonstrates how to maintain a metadata file without interfering with such lock acquisitions. + +The minifilter identifies implicit locks when it sees a non-shared write open request on a volume object. In this scenario, the minifilter closes its metadata file and sets a trigger that corresponds to the volume in its instance object. Later, each close operation is examined to identify if the implicit lock on the volume is being released and, if so, a re-open of the minifilter's metadata file is triggered. + +Similarly, the minifilter might close its metadata file if it sees an explicit FSCTL\_DISMOUNT\_VOLUME or FSCTL\_LOCK\_VOLUME file-system control operation. The file is later opened when the minifilter observes the FSCTL\_UNLOCK\_VOLUME control operation. The IRP\_MN\_QUERY\_REMOVE\_DEVICE PnP request can also cause the minifilter to close its metadata file, and the IRP\_MN\_SURPRISE\_REMOVAL PnP request will cause it to detach. + +The metadata minifilter also handles the case when a snapshot of its volume object is being taken. In this scenario, the minifilter acquires a shared exclusive lock on the metadata resource object while calling the callback that corresponds to the pre-device control operation for IOCTL\_VOLSNAP\_FLUSH\_AND\_HOLD\_WRITES. The lock is later released in the callback that corresponds to the post-device control operation for IOCTL\_VOLSNAP\_FLUSH\_AND\_HOLD\_WRITES. The lock is acquired to prevent any modifications on the metadata file while the snapshot is being taken. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/MetadataManager/fmm.inf b/filesys/miniFilter/MetadataManager/fmm.inf new file mode 100644 index 00000000..f859f9b0 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/fmm.inf @@ -0,0 +1,96 @@ +;;; +;;; Metadata Management File System Filter Driver Sample +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = fmm.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 0 ;SERVICE_BOOT_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugLevel",0x00010001,0x00000001 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +fmm.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Metadata Management File System Filter Driver Sample" +ServiceName = "FMM" +DriverName = "fmm" +DiskId1 = "FMM Device Installation Disk" + +;Instances specific information. +DefaultInstance = "FMM" +Instance1.Name = "FMM" +Instance1.Altitude = "370060" +Instance1.Flags = 0x0 diff --git a/filesys/miniFilter/MetadataManager/fmm.vcxproj b/filesys/miniFilter/MetadataManager/fmm.vcxproj new file mode 100644 index 00000000..c2da0192 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/fmm.vcxproj @@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{A95B7D4F-B926-4E1F-A051-E66091E08D3A}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{DD166882-E368-4576-B537-05B15D55B977}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>fmm</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>fmm</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>fmm</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>fmm</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="DataStore.c" /> + <ClCompile Include="MetadataManagerInit.c" /> + <ClCompile Include="operations.c" /> + <ClCompile Include="support.c" /> + <ResourceCompile Include="MetadataManager.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters b/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters new file mode 100644 index 00000000..b76f8eb2 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{3AEFB0E9-101A-43AB-8E44-D384CB8D17F8}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{58BAA0C3-0CF4-438F-A011-53C6310A7E5E}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{168C91BC-EA8F-4E36-9315-77ACF64E6FA7}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{95E307D5-2257-4A2D-AA47-58C291F7AF68}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="DataStore.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MetadataManagerInit.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="operations.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="support.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="MetadataManager.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/MetadataManager/operations.c b/filesys/miniFilter/MetadataManager/operations.c new file mode 100644 index 00000000..dc9b7c19 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/operations.c @@ -0,0 +1,1356 @@ +/*++ + +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, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + NTSTATUS status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + BOOLEAN isImpliedLock = FALSE; + + 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 + // + + FLT_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 ); + + FLT_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 = FALSE; + + 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 ); + + FLT_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. + // + + FLT_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 + // + + FLT_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") ); + } + + FLT_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, + _Flt_CompletionContext_Outptr_ 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, + ("[Fmm]: FmmPostCleanup -> Enter (Cbd = %p, 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. + // + + FLT_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, + _Flt_CompletionContext_Outptr_ 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. + // + + FLT_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. + // + + FLT_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 + // + + FLT_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. + // + + FLT_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, + _Flt_CompletionContext_Outptr_ 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 + // + + FLT_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, + _Flt_CompletionContext_Outptr_ 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, + _Flt_CompletionContext_Outptr_ 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. + // + + FLT_ASSERT( Cbd->Iopb->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE ); + + // + // Sanity - IRP_MN_CANCEL_DEVICE cannot fail. + // + + FLT_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. + // + + FLT_ASSERT( (status == STATUS_INSUFFICIENT_RESOURCES) || + (status == STATUS_FILE_LOCK_CONFLICT) ); + + } + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + diff --git a/filesys/miniFilter/MetadataManager/pch.h b/filesys/miniFilter/MetadataManager/pch.h new file mode 100644 index 00000000..ed7047d8 --- /dev/null +++ b/filesys/miniFilter/MetadataManager/pch.h @@ -0,0 +1,47 @@ +/*++ + +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 <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> +#include "MetadataManagerStruc.h" +#include "MetadataManagerProc.h" + + +#endif __FMM_PCH_H__ + diff --git a/filesys/miniFilter/MetadataManager/support.c b/filesys/miniFilter/MetadataManager/support.c new file mode 100644 index 00000000..fdfc22cf --- /dev/null +++ b/filesys/miniFilter/MetadataManager/support.c @@ -0,0 +1,244 @@ +/*++ + +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; + } + + FLT_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_REFS: + *IsLock = ((!BooleanFlagOn( shareAccess, FILE_SHARE_WRITE | FILE_SHARE_DELETE)) && + (BooleanFlagOn( prevAccess,(FILE_WRITE_DATA | FILE_APPEND_DATA) ))); + status = STATUS_SUCCESS; + break; + + 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; +} + diff --git a/filesys/miniFilter/avscan/ReadMe.md b/filesys/miniFilter/avscan/ReadMe.md new file mode 100644 index 00000000..f17c27c2 --- /dev/null +++ b/filesys/miniFilter/avscan/ReadMe.md @@ -0,0 +1,8 @@ +AvScan File System Minifilter Driver +==================================== + +The AvScan minifilter is a transaction-aware file scanner. This is an example for developers who intend to write filters that examine data in files. Typically, anti-virus products fall into this category. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + diff --git a/filesys/miniFilter/avscan/avscan.inf b/filesys/miniFilter/avscan/avscan.inf new file mode 100644 index 00000000..e536395e --- /dev/null +++ b/filesys/miniFilter/avscan/avscan.inf @@ -0,0 +1,106 @@ +;;; +;;; AvScan +;;; +;;; +;;; Copyright (c) Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ContentScreener" ;This is determined by the work this filter driver does +ClassGuid = {3e3f0674-c83c-4558-bb26-9820e1eba5c5} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2011,1.0.0.1 +CatalogFile = avscan.cat + + +[DestinationDirs] +DefaultDestDir = 12 +AvScan.DriverFiles = 12 ;%windir%\system32\drivers +AvScan.UserFiles = 10,FltMgr ;%windir%\FltMgr + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = AvScan.DriverFiles, AvScan.UserFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,AvScan.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = AvScan.DriverFiles, AvScan.UserFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[AvScan.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Content Screener" +AddReg = AvScan.AddRegistry + +; +; Registry Modifications +; + +[AvScan.AddRegistry] +HKR,,"DebugFlags",0x00010001,0xc +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% +HKR,,"LocalScanTimeout",0x00010001,%LocalScanTimeout% +HKR,,"NetworkScanTimeout",0x00010001,%NetworkScanTimeout% +HKR,,"SupportedFeatures",0x00010001,0x3 + +; +; Copy Files +; + +[AvScan.DriverFiles] +%DriverName%.sys + +[AvScan.UserFiles] +%UserAppName%.exe + +[SourceDisksFiles] +avscan.sys = 1,, +avscan.exe = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Anti-virus Mini-Filter Driver" +ServiceName = "avscan" +DriverName = "avscan" +UserAppName = "avscan" +DiskId1 = "Anti-virus Device Installation Disk" +LocalScanTimeout = "30000" +NetworkScanTimeout = "60000" + +;Instances specific information. +DefaultInstance = "avscan Instance" +Instance1.Name = "avscan Instance" +Instance1.Altitude = "265010" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/avscan/avscan.sln b/filesys/miniFilter/avscan/avscan.sln new file mode 100644 index 00000000..0fc17fad --- /dev/null +++ b/filesys/miniFilter/avscan/avscan.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{9B4F0964-C698-49FE-B160-251A3A483399}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{4A27FF26-F700-4D97-9F45-F1CBD7C78513}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "filter\avscan.vcxproj", "{9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "user\avscan.vcxproj", "{23D46B81-CF8D-48E5-BF28-3679E6106D7F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|Win32.Build.0 = Debug|Win32 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|Win32.ActiveCfg = Release|Win32 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|Win32.Build.0 = Release|Win32 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|x64.ActiveCfg = Debug|x64 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Debug|x64.Build.0 = Debug|x64 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|x64.ActiveCfg = Release|x64 + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}.Release|x64.Build.0 = Release|x64 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|Win32.ActiveCfg = Debug|Win32 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|Win32.Build.0 = Debug|Win32 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|Win32.ActiveCfg = Release|Win32 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|Win32.Build.0 = Release|Win32 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|x64.ActiveCfg = Debug|x64 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Debug|x64.Build.0 = Debug|x64 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|x64.ActiveCfg = Release|x64 + {23D46B81-CF8D-48E5-BF28-3679E6106D7F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {9D7DE7C5-51FC-4465-B1F9-0B3C9900477A} = {9B4F0964-C698-49FE-B160-251A3A483399} + {23D46B81-CF8D-48E5-BF28-3679E6106D7F} = {4A27FF26-F700-4D97-9F45-F1CBD7C78513} + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/avscan/filter/avscan.c b/filesys/miniFilter/avscan/filter/avscan.c new file mode 100644 index 00000000..e9cab600 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.c @@ -0,0 +1,3152 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + avscan.c + +Abstract: + + This is the main module of the avscan mini-filter driver. + This filter demonstrates how to implement a transaction-aware + anti-virus filter. + + Av prefix denotes "Anti-virus" module. + +Environment: + + Kernel mode + +--*/ + +#include <initguid.h> +#include "avscan.h" + +/************************************************************************* + Local Function Prototypes +*************************************************************************/ + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +AvSetConfiguration ( + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +AvInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +AvInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Unreferenced_parameter_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +AvInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +AvUnload ( + _Unreferenced_parameter_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +AvInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +AvPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +AvPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +AvPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +AvPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +AvPreFsControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +NTSTATUS +AvKtmNotificationCallback ( + _Unreferenced_parameter_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ); + +NTSTATUS +AvScanAbortCallbackAsync ( + _Unreferenced_parameter_ PFLT_INSTANCE Instance, + _In_ PFLT_CONTEXT Context, + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data + ); + +// +// Local routines +// + +BOOLEAN +AvOperationsModifyingFile ( + _In_ PFLT_CALLBACK_DATA Data + ); + +NTSTATUS +AvQueryTransactionOutcome( + _In_ PKTRANSACTION Transaction, + _Out_ PULONG TxOutcome + ); + +NTSTATUS +AvProcessPreviousTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +AvProcessTransactionOutcome ( + _Inout_ PAV_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG TransactionOutcome + ); + +NTSTATUS +AvLoadFileStateFromCache ( + _In_ PFLT_INSTANCE Instance, + _In_ PAV_FILE_REFERENCE FileId, + _Out_ LONG volatile* State, + _Out_ PLONGLONG VolumeRevision, + _Out_ PLONGLONG CacheRevision, + _Out_ PLONGLONG FileRevision + ); + +NTSTATUS +AvSyncCache ( + _In_ PFLT_INSTANCE Instance, + _In_ PAV_STREAM_CONTEXT StreamContext + ); + +BOOLEAN +AvIsPrefetchEcpPresent ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_CALLBACK_DATA Data + ); + +BOOLEAN +AvIsStreamAlternate ( + _Inout_ PFLT_CALLBACK_DATA Data + ); + +NTSTATUS +AvScan ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ AV_SCAN_MODE ScanMode, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _Inout_ PAV_STREAM_CONTEXT StreamContext + ); + +VOID +AvDoCancelScanAndRelease ( + _In_ PAV_SCAN_CONTEXT ScanContext, + _In_ PAV_SECTION_CONTEXT SectionContext + ); + +NTSTATUS +AvSendUnloadingToUser ( + VOID + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, AvSetConfiguration) +#pragma alloc_text(PAGE, AvUnload) +#pragma alloc_text(PAGE, AvInstanceQueryTeardown) +#pragma alloc_text(PAGE, AvInstanceSetup) +#pragma alloc_text(PAGE, AvInstanceTeardownStart) +#pragma alloc_text(PAGE, AvInstanceTeardownComplete) +#pragma alloc_text(PAGE, AvPreCreate) +#pragma alloc_text(PAGE, AvPostCreate) +#pragma alloc_text(PAGE, AvPreFsControl) +#pragma alloc_text(PAGE, AvPreCleanup) +#pragma alloc_text(PAGE, AvKtmNotificationCallback) +#pragma alloc_text(PAGE, AvScanAbortCallbackAsync) +#pragma alloc_text(PAGE, AvOperationsModifyingFile) +#pragma alloc_text(PAGE, AvQueryTransactionOutcome) +#pragma alloc_text(PAGE, AvProcessPreviousTransaction) +#pragma alloc_text(PAGE, AvProcessTransactionOutcome) +#pragma alloc_text(PAGE, AvLoadFileStateFromCache) +#pragma alloc_text(PAGE, AvSyncCache) +#pragma alloc_text(PAGE, AvIsPrefetchEcpPresent) +#pragma alloc_text(PAGE, AvIsStreamAlternate) +#pragma alloc_text(PAGE, AvScan) +#pragma alloc_text(PAGE, AvDoCancelScanAndRelease) +#pragma alloc_text(PAGE, AvSendAbortToUser) +#pragma alloc_text(PAGE, AvSendUnloadingToUser) +#endif + +// +// operation registration +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + AvPreCreate, + AvPostCreate }, + + { IRP_MJ_CLEANUP, + 0, + AvPreCleanup, + NULL }, + + { IRP_MJ_WRITE, + 0, + AvPreOperationCallback, + NULL }, + + { IRP_MJ_SET_INFORMATION, + 0, + AvPreOperationCallback, + NULL }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + AvPreFsControl, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Context registraction construct defined in context.c +// + +extern const FLT_CONTEXT_REGISTRATION ContextRegistration[]; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + ContextRegistration, // Context + Callbacks, // Operation callbacks + + AvUnload, // MiniFilterUnload + + AvInstanceSetup, // InstanceSetup + AvInstanceQueryTeardown, // InstanceQueryTeardown + AvInstanceTeardownStart, // InstanceTeardownStart + AvInstanceTeardownComplete, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // NormalizeNameComponentCallback + NULL, // NormalizeContextCleanupCallback + AvKtmNotificationCallback, // TransactionNotificationCallback + NULL, // NormalizeNameComponentExCallback + AvScanAbortCallbackAsync // SectionNotificationCallback +}; + + + +NTSTATUS +AvInstanceSetup ( + _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. + + If this routine is not defined in the registration structure, automatic + instances are alwasys created. + +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. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + NTSTATUS status; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + BOOLEAN isOnCsv = FALSE; + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvInstanceSetup: Entered\n") ); + + // + // Don't attach to network volumes. + // + + if (VolumeDeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Determine if the filter is attaching to the hidden NTFS volume + // that corresponds to a CSV volume. If so do not attach. Note + // that it would be feasible for the filter to attach to this + // volume as part of a distrubuted filter implementation but that + // is beyond the scope of this sample. + // + + if (VolumeFilesystemType == FLT_FSTYPE_NTFS) { + isOnCsv = AvIsVolumeOnCsvDisk( FltObjects->Volume ); + if (isOnCsv) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + } + + status = FltAllocateContext( Globals.Filter, + FLT_INSTANCE_CONTEXT, + AV_INSTANCE_CONTEXT_SIZE, + NonPagedPoolNx, + &instanceContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvInstanceSetup: allocate instance context failed. status = 0x%x\n", status) ); + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Setup instance context + // + + RtlZeroMemory(instanceContext, AV_INSTANCE_CONTEXT_SIZE); + instanceContext->Volume = FltObjects->Volume; + instanceContext->Instance = FltObjects->Instance; + instanceContext->VolumeFSType = VolumeFilesystemType; + instanceContext->IsOnCsvMDS = isOnCsv; + + // + // There will be a file state cache table for each NTFS volume instance. + // As for other file systems, file id is not unique, and thus we do + // not have cache for other kinds of file systems. Since the cache + // table is not mandatory to implement an anti-virus filter, we + // only have the volatile cache for NTFS, CSVFS and REFS. + // + // It is worth mentioning that the table is potentially very large. + // We use an AVL tree to improve insertion and query times. We do not + // set an upper bound for the size of the tree which is not optimal. + // Consider limiting the size of the tree for a production filter. + // + + if (FS_SUPPORTS_FILE_STATE_CACHE( VolumeFilesystemType )) { + + // + // Initialize file state cache in the instance context. + // + + ExInitializeResourceLite( &instanceContext->Resource ); + + RtlInitializeGenericTable( &instanceContext->FileStateCacheTable, + (PRTL_GENERIC_COMPARE_ROUTINE) AvCompareEntry, + (PRTL_GENERIC_ALLOCATE_ROUTINE) AvAllocateGenericTableEntry, + (PRTL_GENERIC_FREE_ROUTINE) AvFreeGenericTableEntry, + NULL ); + } + + status = FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + instanceContext, + NULL ); + + // + // In all cases, we need to release the instance context at this time. + // If we hit an error, it will get freed now. + // + + FltReleaseContext( instanceContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvInstanceSetup: set instance context failed. status = 0x%x\n", status) ); + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Register this instance as a datascan filter. If this call + // fails the underlying filesystem does not support using + // the filter manager datascan API. Currently only the + // the namedpipe and mailslot file systems are unsupported. + // + + status = FltRegisterForDataScan( FltObjects->Instance ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvInstanceSetup: FltRegisterForDataScan failed. status = 0x%x\n", status) ); + return STATUS_FLT_DO_NOT_ATTACH; + + } + + return STATUS_SUCCESS; +} + +NTSTATUS +AvInstanceQueryTeardown ( + _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. + + If this routine is not defined in the registration structure, explicit + detach requests via FltDetachVolume or FilterDetach will always be + failed. + +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(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvInstanceQueryTeardown: Entered\n") ); + + return STATUS_SUCCESS; +} + +VOID +AvInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Unreferenced_parameter_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is called at the start of instance teardown. + If we have cache table, we have to clean up the table at this point. + +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. + +--*/ +{ + NTSTATUS status; + PLIST_ENTRY scan; + PLIST_ENTRY next; + PAV_SCAN_CONTEXT scanCtx = NULL; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvInstanceTeardownStart: Entered\n") ); + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvInstanceTeardownStart: FltGetInstanceContext failed. status = 0x%x\n", status) ); + return; + } + + // + // Search the scan context from the global list. + // + + AvAcquireResourceExclusive( &Globals.ScanCtxListLock ); + + LIST_FOR_EACH_SAFE( scan, next, &Globals.ScanCtxListHead ) { + + scanCtx = CONTAINING_RECORD( scan, AV_SCAN_CONTEXT, List ); + + if (scanCtx->FilterInstance != FltObjects->Instance) { + + continue; + } + + // + // Notify the user scan thread to abort the scan. + // + status = AvSendAbortToUser(scanCtx->ScanThreadId, + scanCtx->ScanId); + + + // + // If we fail to send message to the user, then we + // do the cancel and cleanup by ourself; otherwise, + // the listening thread will call back to cleanup and + // I/O request thred will tear down the scan context. + // + + if (!NT_SUCCESS( status ) || status == STATUS_TIMEOUT) { + + AvFinalizeScanAndSection(scanCtx); + } + } + + AvReleaseResource( &Globals.ScanCtxListLock ); + + // + // Clean up the cache table if the volume supports one. + // + + if (FS_SUPPORTS_FILE_STATE_CACHE( instanceContext->VolumeFSType )) { + PAV_GENERIC_TABLE_ENTRY entry = NULL; + AvAcquireResourceExclusive( &instanceContext->Resource ); + + while (!RtlIsGenericTableEmpty( &instanceContext->FileStateCacheTable ) ) { + entry = RtlGetElementGenericTable(&instanceContext->FileStateCacheTable, 0); + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvInstanceTeardownStart: %I64x,%I64x requesting deletion, state:%d\n", + entry->FileId.FileId64.UpperZeroes, + entry->FileId.FileId64.Value, + entry->InfectedState) ); + RtlDeleteElementGenericTable(&instanceContext->FileStateCacheTable, entry); + } + + AvReleaseResource( &instanceContext->Resource ); + } + + FltReleaseContext( instanceContext ); + + FltDeleteInstanceContext( FltObjects->Instance, NULL ); +} + +VOID +AvInstanceTeardownComplete ( + _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 ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvInstanceTeardownComplete: Entered\n") ); +} + + +/************************************************************************* + MiniFilter initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all 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 the final status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PSECURITY_DESCRIPTOR sd = NULL; + + UNREFERENCED_PARAMETER( RegistryPath ); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] DriverEntry: Entered\n") ); + + // + // Set default global configuration + // + + RtlZeroMemory( &Globals, sizeof(Globals) ); + InitializeListHead( &Globals.ScanCtxListHead ); + ExInitializeResourceLite( &Globals.ScanCtxListLock ); + + Globals.ScanIdCounter = 0; + Globals.LocalScanTimeout = 30000; + Globals.NetworkScanTimeout = 60000; + +#if DBG + + Globals.DebugLevel = 0xffffffff; // AVDBG_TRACE_ERROR | AVDBG_TRACE_DEBUG; + +#endif + + try { + + // + // Set the filter configuration based on registry keys + // + + status = AvSetConfiguration( RegistryPath ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: DriverEntry: SetConfiguration FAILED. status = 0x%x\n", status) ); + + leave; + } + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: FltRegisterFilter FAILED. status = 0x%x\n", status) ); + leave; + } + + // + // Builds a default security descriptor for use with FltCreateCommunicationPort. + // + + status = FltBuildDefaultSecurityDescriptor( &sd, + FLT_PORT_ALL_ACCESS ); + + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: FltBuildDefaultSecurityDescriptor FAILED. status = 0x%x\n", status) ); + leave; + } + // + // Prepare ports between kernel and user. + // + + status = AvPrepareServerPort( sd, AvConnectForScan ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: AvPrepareServerPort Scan Port FAILED. status = 0x%x\n", status) ); + leave; + } + + status = AvPrepareServerPort( sd, AvConnectForAbort ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: AvPrepareServerPort Abort Port FAILED. status = 0x%x\n", status) ); + leave; + } + + status = AvPrepareServerPort( sd, AvConnectForQuery ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: AvPrepareServerPort Query Port FAILED. status = 0x%x\n", status) ); + leave; + } + + // + // Start filtering i/o + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] DriverEntry: FltStartFiltering FAILED. status = 0x%x\n", status) ); + leave; + } + + } finally { + + if ( sd != NULL ) { + + FltFreeSecurityDescriptor( sd ); + } + + if (!NT_SUCCESS( status ) ) { + + if (NULL != Globals.ScanServerPort) { + + FltCloseCommunicationPort( Globals.ScanServerPort ); + } + if (NULL != Globals.AbortServerPort) { + + FltCloseCommunicationPort( Globals.AbortServerPort ); + } + if (NULL != Globals.QueryServerPort) { + + FltCloseCommunicationPort( Globals.QueryServerPort ); + } + if (NULL != Globals.Filter) { + + FltUnregisterFilter( Globals.Filter ); + Globals.Filter = NULL; + } + + ExDeleteResourceLite( &Globals.ScanCtxListLock ); + } + } + + return status; +} + +NTSTATUS +AvUnload ( + _Unreferenced_parameter_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter 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. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Flags ); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvUnload: Entered\n") ); + + // + // Traverse the scan context list, and cancel the scan if it exists. + // + + AvAcquireResourceExclusive( &Globals.ScanCtxListLock ); + Globals.Unloading = TRUE; + AvReleaseResource( &Globals.ScanCtxListLock ); + + // + // This function will wait for the user to abort the outstanding scan and + // close the section + // + + AvSendUnloadingToUser(); + + FltCloseCommunicationPort( Globals.ScanServerPort ); + Globals.ScanServerPort = NULL; + FltCloseCommunicationPort( Globals.AbortServerPort ); + Globals.AbortServerPort = NULL; + FltCloseCommunicationPort( Globals.QueryServerPort ); + Globals.QueryServerPort = NULL; + FltUnregisterFilter( Globals.Filter ); // This will typically trigger instance tear down. + Globals.Filter = NULL; + + ExDeleteResourceLite( &Globals.ScanCtxListLock ); + + return STATUS_SUCCESS; +} + + +/************************************************************************* + Local utility routines. +*************************************************************************/ + +BOOLEAN +AvOperationsModifyingFile ( + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This identifies those operations we need to set the file to be modified. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - If we want the file associated with the request to be modified. + FALSE - If we don't + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + + PAGED_CODE(); + + switch(iopb->MajorFunction) { + + case IRP_MJ_WRITE: + return TRUE; + + case IRP_MJ_FILE_SYSTEM_CONTROL: + switch ( iopb->Parameters.FileSystemControl.Common.FsControlCode ) { + case FSCTL_OFFLOAD_WRITE: + case FSCTL_WRITE_RAW_ENCRYPTED: + case FSCTL_SET_ZERO_DATA: + return TRUE; + default: break; + } + break; + + case IRP_MJ_SET_INFORMATION: + switch ( iopb->Parameters.SetFileInformation.FileInformationClass ) { + case FileEndOfFileInformation: + case FileValidDataLengthInformation: + return TRUE; + default: break; + } + break; + default: + break; + } + return FALSE; +} + +NTSTATUS +AvQueryTransactionOutcome( + _In_ PKTRANSACTION Transaction, + _Out_ PULONG TxOutcome + ) +/*++ + +Routine Description: + + This is a helper function that qeury the KTM that how trasnaction was ended. + +Arguments: + + Transaction - Pointer to transaction object. + + TxOutcome - Output. Specifies the type of transaction outcome. + +Return Value: + + The status of the operation +--*/ +{ + HANDLE transactionHandle; + NTSTATUS status; + TRANSACTION_BASIC_INFORMATION txBasicInfo = {0}; + + PAGED_CODE(); + + status = ObOpenObjectByPointer( Transaction, + OBJ_KERNEL_HANDLE, + NULL, + GENERIC_READ, + *TmTransactionObjectType, + KernelMode, + &transactionHandle ); + + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvQueryTransactionOutcome: ObOpenObjectByPointer failed.\n") ); + return status; + } + + status = ZwQueryInformationTransaction( transactionHandle, + TransactionBasicInformation, + &txBasicInfo, + sizeof(TRANSACTION_BASIC_INFORMATION), + NULL ); + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvQueryTransactionOutcome: ObOpenObjectByPointer failed.\n") ); + goto Cleanup; + } + + *TxOutcome = txBasicInfo.Outcome; + +Cleanup: + + ZwClose(transactionHandle); + + return status; +} + +FORCEINLINE +VOID +AvPropagateFileState( + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _In_ ULONG TransactionOutcome + ) +/*++ + +Routine Description: + + An inline function that propagate the TxState to State in stream context. + +Arguments: + + StreamContext - The stream context to be propagated. + + TransactionOutcome - TRANSACTION_OUTCOME enumeration indicating how transaction was ended. + + +Return Value: + + None. + +--*/ +{ + // + // Only when the transaction was committed will we propagate the state. + // + + if (TransactionOutcome == TransactionOutcomeCommitted) { + + AV_FILE_INFECTED_STATE oldTxState = InterlockedExchange( &StreamContext->TxState, AvFileModified ); + switch (oldTxState) { + case AvFileModified: + case AvFileInfected: + case AvFileNotInfected: + + // + // Propagate the file state from TxState to State. + // + + InterlockedExchange( &StreamContext->State, oldTxState ); + break; + case AvFileScanning: + + // + // It is possible at KTM callback, file Tx state is still in scanning. + // All we can do here is to be conservative, that is to assume that + // this commit did involve the modification of the file. + // + + InterlockedExchange( &StreamContext->State, AvFileModified ); + break; + default: + FLT_ASSERTMSG("AvPropagateFileState does not handle the state", FALSE); + break; + } + } + + // + // Either cleanup or commited, we need to reset TxState to be default state. + // + + SET_FILE_TX_MODIFIED( StreamContext ); +} + +NTSTATUS +AvProcessTransactionOutcome ( + _Inout_ PAV_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG TransactionOutcome + ) +/*++ + +Routine Description: + + This is a helper function that process transaction commitment or rollback + +Arguments: + + TransactionContext - Pointer to the minifilter driver's transaction context + set at PostCreate. + + TransactionOutcome - Specifies the type of notifications. Should be either + TransactionOutcomeCommitted or TransactionOutcomeAborted + +Return Value: + + STATUS_SUCCESS - Returning this status value indicates that the minifilter + driver is finished with the transaction. This is a success code. + +--*/ +{ + PLIST_ENTRY scan; + PLIST_ENTRY next; + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_TRANSACTION_CONTEXT oldTxCtx = NULL; + + PAGED_CODE(); + + // + // Tranversing the stream context list, and + // sync the TxState -> State. + // + // Either commit or rollback, we need to cleanup the list + // Tear down stream context list inside transactionContext + // + + AvAcquireResourceExclusive( TransactionContext->Resource ); + + LIST_FOR_EACH_SAFE( scan, next, &TransactionContext->ScListHead ) { + + streamContext = CONTAINING_RECORD( scan, AV_STREAM_CONTEXT, ListInTransaction ); + oldTxCtx = InterlockedCompareExchangePointer( &streamContext->TxContext, NULL, TransactionContext ); + if (oldTxCtx == TransactionContext) { + + // + // The exchange pointer was successful + // + + RemoveEntryList ( scan ); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvProcessTransactionOutcome: Requesting deletion of entry in transaction context: %I64x,%I64x, modified: %d\n", + streamContext->FileId.FileId64.UpperZeroes, + streamContext->FileId.FileId64.Value, + IS_FILE_MODIFIED( streamContext ) ) ); + AvPropagateFileState( streamContext, TransactionOutcome ); + FltReleaseContext( oldTxCtx ); + FltReleaseContext( streamContext ); + } + } + SetFlag( TransactionContext->Flags, AV_TXCTX_LISTDRAINED ); + AvReleaseResource( TransactionContext->Resource ); + + return STATUS_SUCCESS; +} + +NTSTATUS +AvLoadFileStateFromCache ( + _In_ PFLT_INSTANCE Instance, + _In_ PAV_FILE_REFERENCE FileId, + _Out_ LONG volatile *State, + _Out_ PLONGLONG VolumeRevision, + _Out_ PLONGLONG CacheRevision, + _Out_ PLONGLONG FileRevision + ) +/*++ + +Routine Description: + + This routine lookups the file state in the cache table. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileID - The ID to lookup in the cache + + State - The cached state for the file + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + AV_GENERIC_TABLE_ENTRY query = {0}; + PAV_GENERIC_TABLE_ENTRY entry = NULL; + + PAGED_CODE(); + + // + // We should never be trying to cache with an invalid fileID. + // + + ASSERT( !AV_INVALID_FILE_REFERENCE(*FileId) ); + + status = FltGetInstanceContext( Instance, + &instanceContext ); + + if (!NT_SUCCESS( status )){ + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvLoadFileStateFromCache: failed to get instance context.\n") ); + return status; + } + + if (! FS_SUPPORTS_FILE_STATE_CACHE( instanceContext->VolumeFSType )) { + + status = STATUS_NOT_FOUND; + goto Cleanup; + } + + RtlCopyMemory( &query.FileId, FileId, sizeof(query.FileId) ); + + AvAcquireResourceShared( &instanceContext->Resource ); + + entry = RtlLookupElementGenericTable( &instanceContext->FileStateCacheTable, + &query ); + + if (entry != NULL) { + *State = entry->InfectedState; + *VolumeRevision = entry->VolumeRevision; + *CacheRevision = entry->CacheRevision; + *FileRevision = entry->FileRevision; + } else { + status = STATUS_NOT_FOUND; + } + + AvReleaseResource( &instanceContext->Resource ); + +Cleanup: + + FltReleaseContext( instanceContext ); + return status; +} + +NTSTATUS +AvSyncCache ( + _In_ PFLT_INSTANCE Instance, + _In_ PAV_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine sync the file state from stream context to volatile cache table. + It is file system transparent. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + StreamContext - The stream context of the target file. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN inserted = FALSE; + AV_GENERIC_TABLE_ENTRY entry = {0}; + PAV_GENERIC_TABLE_ENTRY pEntry = NULL; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + if ((NULL == Instance) || + (NULL == StreamContext)) { + + return STATUS_INVALID_PARAMETER; + } + + status = FltGetInstanceContext( Instance, &instanceContext ); + + if (!NT_SUCCESS( status )){ + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvSyncCache: failed to get instance context.\n") ); + return status; + } + + // + // If the file system is not NTFS, CSVFS or REFS, do nothing + // + + if (!FS_SUPPORTS_FILE_STATE_CACHE( instanceContext->VolumeFSType )) { + goto Cleanup; + } + + // + // If originally, we failed to get the file id, + // then we do not cache it. + // + + if (AV_INVALID_FILE_REFERENCE( StreamContext->FileId )) { + goto Cleanup; + } + + // + // If the file system is NTFS, CSVFS or REFS, overwrite the entry in the + // cache table if exists + // + + RtlCopyMemory( &entry.FileId, &StreamContext->FileId, sizeof(entry.FileId) ); + + AvAcquireResourceExclusive( &instanceContext->Resource ); + + pEntry = RtlInsertElementGenericTable( &instanceContext->FileStateCacheTable, + (PVOID) &entry, + AV_GENERIC_TABLE_ENTRY_SIZE, + &inserted); + if (pEntry) { + + // + // Note the cache may become stale as files are modified. + // + + // + // It is possible that after entering the following else-if + // branch, thread A modifies the file, and before thread A + // closes the handle, thread B opens the same file. This + // is fine because in such a case, the streamcontext exists + // AvLoadFileStateFromCache would return the state in stream + // context. Thus, thread B will need to scan the file. + // + + pEntry->InfectedState = StreamContext->State; + pEntry->VolumeRevision = StreamContext->VolumeRevision; + pEntry->CacheRevision = StreamContext->CacheRevision; + pEntry->FileRevision = StreamContext->FileRevision; + + } + + AvReleaseResource( &instanceContext->Resource ); + + if (!pEntry) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvSyncCache: RtlInsertElementGenericTable failed.\n") ); + } + +Cleanup: + + FltReleaseContext( instanceContext ); + return status; +} + +BOOLEAN +AvIsPrefetchEcpPresent ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This local function will return if this data stream is alternate or not. + It by default returns FALSE if it fails to retrieve the name information + from the file system. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - This data stream is alternate. + FALSE - This data stream is NOT alternate. + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList; + PVOID ecpContext; + + PAGED_CODE(); + + status = FltGetEcpListFromCallbackData( Filter, Data, &ecpList ); + + if (NT_SUCCESS(status) && (ecpList != NULL)) { + + status = FltFindExtraCreateParameter( Filter, + ecpList, + &GUID_ECP_PREFETCH_OPEN, + &ecpContext, + NULL ); + + if (NT_SUCCESS(status)) { + + if (!FltIsEcpFromUserMode( Filter, ecpContext )) { + return TRUE; + } + } + } + + return FALSE; +} + +BOOLEAN +AvIsStreamAlternate( + _Inout_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This local function will return if this data stream is alternate or not. + It by default returns FALSE if it fails to retrieve the name information + from the file system. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - This data stream is alternate. + FALSE - This data stream is NOT alternate. + +--*/ +{ + NTSTATUS status; + BOOLEAN alternate = FALSE; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + + PAGED_CODE(); + + status = FltGetFileNameInformation( Data, + FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, + &nameInfo ); + + if (!NT_SUCCESS(status)) { + + goto Cleanup; + } + + status = FltParseFileNameInformation( nameInfo ); + + if (!NT_SUCCESS(status)) { + + goto Cleanup; + } + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[Av]: Dir: %wZ, FinalComponent: %wZ, Stream: %wZ, sLen: %d\n", + nameInfo->ParentDir, + nameInfo->FinalComponent, + nameInfo->Stream, + nameInfo->Stream.Length) ); + + alternate = (nameInfo->Stream.Length > 0); + +Cleanup: + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + nameInfo = NULL; + } + return alternate; +} + +NTSTATUS +AvScan ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ AV_SCAN_MODE ScanMode, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _Inout_ PAV_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine kicks of a scan. + +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. + + ScanMode - Can either be AvUserMode or AvKernelMode. + + IOMajorFunctionAtScan - Major function of an IRP. + + StreamContext - The stream context of the target file. + +Return Value: + + Returns the final status of this operation. + STATUS_TIMEOUT - if scan in user mode and the thread reference fails, + then it would wait for the scan finish event with a timeout. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + LONGLONG fileSize; + FLT_VOLUME_PROPERTIES volumeProperties; + ULONG volumePropertiesLength; + + PAGED_CODE(); + + // + // Skip the empty file. + // + + status = AvGetFileSize( FltObjects->Instance, + FltObjects->FileObject, + &fileSize ); + + if (NT_SUCCESS( status ) && + (0 == fileSize)) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[Av]: AvScan: Skip the EMPTY file.\n") ); + + // As if we have 'scanned' this empty file. + SET_FILE_NOT_INFECTED( StreamContext ); + return STATUS_SUCCESS; + } + + // + // We could cause deadlocks if the thread were suspended once + // we have started scanning so enter a critical region. + // + + FsRtlEnterFileSystem(); + + // + // Wait here for an existing scan on the stream to complete. + // We wait indefinitely since scans themselves will timeout. + // + + status = FltCancellableWaitForSingleObject( StreamContext->ScanSynchronizationEvent, + NULL, + Data ); + + if (NT_SUCCESS(status)) { + + // + // Check again in case the file was scanned during the wait + // and is already known to be clean + // + + if (IS_FILE_NEED_SCAN( StreamContext )){ + + if (ScanMode == AvUserMode) { + + status = FltGetVolumeProperties( FltObjects->Volume, + &volumeProperties, + sizeof(volumeProperties), + &volumePropertiesLength ); + if (!NT_SUCCESS(status)) { + volumeProperties.DeviceType = FILE_DEVICE_NETWORK; + } + + // + // If the scan mode is user mode, the section context will + // be created as needed (at MessageNotification callback). + // + // Setting the file state will be done at + // MessageNotification callback as well. + // + + status = AvScanInUser( Data, + FltObjects, + IOMajorFunctionAtScan, + IsInTxWriter, + volumeProperties.DeviceType ); + + if (!NT_SUCCESS( status ) || status == STATUS_TIMEOUT) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvScan: failed to scan the file.\n") ); + } + + } else { + + status = AvScanInKernel( FltObjects, + IOMajorFunctionAtScan, + IsInTxWriter, + StreamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvScan: failed to scan the file.\n") ); + } + + } + + } + + // + // Signal ScanSynchronizationEvent to release any con-current scan of the stream, + // + KeSetEvent( StreamContext->ScanSynchronizationEvent, 0, FALSE ); + + } else if (IOMajorFunctionAtScan == IRP_MJ_CREATE) { + + // + // I/O requesting thread if waiting on synchronization event is cancelled, + // we need to clean up the file object too. + // + AvCancelFileOpen(Data, FltObjects, status); + } + + FsRtlExitFileSystem(); + + return status; +} + +VOID +AvDoCancelScanAndRelease ( + _In_ PAV_SCAN_CONTEXT ScanContext, + _In_ PAV_SECTION_CONTEXT SectionContext + ) +/*++ + +Routine Description: + + This routine closes the section object, and released all waiting threads. + +Arguments: + + ScanContext - The scan context. + + SectionContext - The section context associated with the scan context. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + PAV_STREAM_CONTEXT streamContext = NULL; + + PAGED_CODE(); + + AvFinalizeSectionContext( SectionContext ); + + status = FltGetStreamContext( ScanContext->FilterInstance, + ScanContext->FileObject, + &streamContext ); + + if (NT_SUCCESS( status )) { + + KeSetEvent( streamContext->ScanSynchronizationEvent, 0, FALSE ); + FltReleaseContext( streamContext ); + } + + // + // Release I/O request thread. + // + + KeSetEvent( &ScanContext->ScanCompleteNotification, 0, FALSE ); + return; +} + +NTSTATUS +AvSendAbortToUser ( + _In_ ULONG ScanThreadId, + _In_ LONGLONG ScanId + ) +/*++ + +Routine Description: + + This routine sends an abortion message to the user scan thread. + The cancel callback is asynchronous and thus we send which + scan id to abort; otherwise the worker thread in the user + may abort the 'next' scan task. + +Arguments: + + ScanThreadId - The thread identifier of whom to be aborted. + + ScanId - Which scan task to be aborted. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG replyLength = 0; + LARGE_INTEGER timeout = {0}; + AV_SCANNER_NOTIFICATION notification = {0}; + + PAGED_CODE(); + + notification.Message = AvMsgAbortScanning; + notification.ScanThreadId = ScanThreadId; + notification.ScanId = ScanId; + + timeout.QuadPart = -((LONGLONG)10) * (LONGLONG)1000 * (LONGLONG)1000; // 1s + + // + // Tell the user-scanner to abort the scan. + // + + status = FltSendMessage( Globals.Filter, + &Globals.AbortClientPort, + ¬ification, + sizeof(AV_SCANNER_NOTIFICATION), + NULL, + &replyLength, + &timeout ); + + + if (!NT_SUCCESS( status ) || + (status == STATUS_TIMEOUT)) { + + if ((status != STATUS_PORT_DISCONNECTED) && + (status != STATUS_TIMEOUT)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvSendAbortToUser: Failed to FltSendMessage.\n, 0x%08x\n", + status) ); + } + return status; + } + return status; +} + +NTSTATUS +AvSendUnloadingToUser ( + VOID + ) +/*++ + +Routine Description: + + This routine sends unloading message to the user program. + +Arguments: + + None. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + ULONG abortThreadId; + NTSTATUS status = STATUS_SUCCESS; + ULONG replyLength = sizeof(ULONG); + AV_SCANNER_NOTIFICATION notification = {0}; + + PAGED_CODE(); + + notification.Message = AvMsgFilterUnloading; + + // + // Tell the user-scanner that we are unloading the filter. + // and waits for its reply. + // + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvSendUnloadingToUser: BEFORE...\n") ); + + status = FltSendMessage( Globals.Filter, + &Globals.AbortClientPort, + ¬ification, + sizeof(AV_SCANNER_NOTIFICATION), + &abortThreadId, + &replyLength, + NULL ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvSendUnloadingToUser: Failed to FltSendMessage.\n, 0x%08x\n", + status) ); + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvSendUnloadingToUser: After...\n") ); + + return status; +} + +/************************************************************************* + MiniFilter callback routines. +*************************************************************************/ + +FLT_PREOP_CALLBACK_STATUS +AvPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the registered callback routine for filtering + the "write" operation, i.e. the operations that have potentials + to modify the file. + + This is non-pageable because it could be called on the paging path + +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 - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + ULONG flags; + + UNREFERENCED_PARAMETER( CompletionContext ); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreOperationCallback: Entered\n") ); + + if (!AvOperationsModifyingFile(Data)) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip prefetcher handles to avoid deadlocks + // + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &streamHandleContext ); + if (NT_SUCCESS(status)) { + + flags = streamHandleContext->Flags; + + FltReleaseContext( streamHandleContext ); + + if (FlagOn( flags, AV_FLAG_PREFETCH )) { + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + } + + status = FltGetStreamContext( FltObjects->Instance, + FltObjects->FileObject, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreOperationCallback: get stream context failed. rq: %d\n", + Data->Iopb->MajorFunction) ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // If this operation is performed in a transacted writer view. + // + + if ((streamContext->TxContext != NULL) && + (FltObjects->Transaction != NULL)) { + +#if DBG + PAV_TRANSACTION_CONTEXT transactionContext = NULL; + + NTSTATUS statusTx = FltGetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + FLT_ASSERTMSG( "Transaction context should not fail, because it is supposed to be created at post create.\n", NT_SUCCESS( statusTx )); + FLT_ASSERTMSG( "The file's TxCtx should be identical with the target TxCtx.\n", + streamContext->TxContext == transactionContext); + + if (NT_SUCCESS( statusTx )) { + FltReleaseContext( transactionContext ); + } + +#endif // DBG + + // + // Instead of updating State, we update TxState here, + // because the file is part of a transaction writer + // + + SET_FILE_TX_MODIFIED( streamContext ); + + } else { + + // + // Consider an optimization for the case where another thread + // is already scanning the file as it is being modified here. + // + + SET_FILE_MODIFIED( streamContext ); + } + + FltReleaseContext( streamContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + +FLT_PREOP_CALLBACK_STATUS +AvPreFsControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre-file system control callback. This filter example does not support save point feature. + So, we explicitly fail the request here. + +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 - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + + PAGED_CODE(); + + if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_TXFS_SAVEPOINT_INFORMATION ) { + + // + // We explicitly fail the request of save point here since we + // are deprecating savepoint support for the OS version targeted + // for this filter. + // + + Data->IoStatus.Status = STATUS_NOT_SUPPORTED; + return FLT_PREOP_COMPLETE; + } + return AvPreOperationCallback(Data, FltObjects, CompletionContext); +} + +FLT_PREOP_CALLBACK_STATUS +AvPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-create completion routine. + + +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 - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + FLT_PREOP_SYNCHRONIZE - PostCreate needs to be called back synchronizedly. + FLT_PREOP_SUCCESS_NO_CALLBACK - PostCreate does not need to be called. + +--*/ +{ + ULONG_PTR stackLow; + ULONG_PTR stackHigh; + PFILE_OBJECT FileObject = Data->Iopb->TargetFileObject; + AV_STREAMHANDLE_CONTEXT streamHandleContext; + + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreCreate: Entered\n") ); + + streamHandleContext.Flags = 0; + + // + // Stack file objects are never scanned. + // + + IoGetStackLimits( &stackLow, &stackHigh ); + + if (((ULONG_PTR)FileObject > stackLow) && + ((ULONG_PTR)FileObject < stackHigh)) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Directory opens don't need to be scanned. + // + + if (FlagOn( Data->Iopb->Parameters.Create.Options, FILE_DIRECTORY_FILE )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip pre-rename operations which always open a directory. + // + + if ( FlagOn( Data->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip paging files. + // + + if (FlagOn( Data->Iopb->OperationFlags, SL_OPEN_PAGING_FILE )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip scanning DASD opens + // + + if (FlagOn( FltObjects->FileObject->Flags, FO_VOLUME_OPEN )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Skip scanning any files being opened by CSVFS for its downlevel + // processing. This includes filters on the hidden NTFS stack and + // for filters attached to MUP + // + if (AvIsCsvDlEcpPresent( FltObjects->Filter, Data ) ) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + + // + // Flag prefetch handles so they can be skipped. Performing IO + // using a prefetch fileobject could lead to a deadlock. + // + + if (AvIsPrefetchEcpPresent( FltObjects->Filter, Data )) { + + SetFlag( streamHandleContext.Flags, AV_FLAG_PREFETCH ); + } + + *CompletionContext = (PVOID)streamHandleContext.Flags; + + // + // Perform any CSVFS pre create processing + // + AvPreCreateCsvfs( Data, FltObjects ); + + // + // return status can be safely ignored + // + + // + // Return FLT_PREOP_SYNCHRONIZE at PreCreate to ensure PostCreate + // is in the same thread at passive level. + // EResource can't be acquired at DPC. + // + + return FLT_PREOP_SYNCHRONIZE; + +} + +NTSTATUS +AvProcessPreviousTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine is transaction related implmentation, and is expected to be + invoked at post-create. Note that this function will enlist the newly + allocated transaction context via FltEnlistInTransaction if it needs to. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - The stream context. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_TRANSACTION_CONTEXT oldTxCtx = NULL; + PAV_TRANSACTION_CONTEXT transactionContext = NULL; + + PAGED_CODE(); + + if (FltObjects->Transaction != NULL ) { + + // + // Get transaction context + // + + status = AvFindOrCreateTransactionContext( FltObjects, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvProcessPreviousTransaction: AvFindOrCreateTransactionContext FAILED\n") ); + transactionContext = NULL; + goto Cleanup; + } + + // + // Enlist it if haven't. + // + + if (! FlagOn(transactionContext->Flags, AV_TXCTX_ENLISTED) ) { + + // + // You can also consider to register TRANSACTION_NOTIFY_PREPARE, + // and scan the file at TRANSACTION_NOTIFY_PREPARE callback if it was modified. + // + + status = FltEnlistInTransaction( FltObjects->Instance, + FltObjects->Transaction, + transactionContext, + TRANSACTION_NOTIFY_COMMIT_FINALIZE | TRANSACTION_NOTIFY_ROLLBACK ); + + if (!NT_SUCCESS( status ) && + (status != STATUS_FLT_ALREADY_ENLISTED)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvProcessPreviousTransaction: FltEnlistInTransaction FAILED!!!!\n") ); + goto Cleanup; + } + status = STATUS_SUCCESS; + SetFlag( transactionContext->Flags, AV_TXCTX_ENLISTED ); + } + } + + // + // Here we have five cases: + // + // 1) + // oldTxCtx : NULL + // transCtx : B + // 2) + // oldTxCtx : A + // transCtx : NULL + // 3) + // oldTxCtx : A + // transCtx : B + // 4) + // oldTxCtx : A + // transCtx : A + // 5) + // oldTxCtx : NULL + // transCtx : NULL + // + + // + // Synchronize the replacement of StreamContext->TxContext with KTM callback. + // + + oldTxCtx = InterlockedExchangePointer( &StreamContext->TxContext, transactionContext ); + + if (oldTxCtx != transactionContext) { // case 1,2,3 + + // + // txOutcome is by default set as committed because we are conservative about + // propagating the file state if AvQueryTransactionOutcome failed, it may cause + // redundant scan but will not overlook infected file anyway. + // + + ULONG txOutcome = TransactionOutcomeCommitted; + + if ( oldTxCtx == NULL ) { // case 1 + + // This file was not linked in a transaction context yet, and is about to. + // + // Increment TxContext's reference count because stream context has a reference to it. + // + + FltReferenceContext ( transactionContext ); + + // + // Before insertion into the FcList in transaction context, we increment stream context's ref count + // + + AvAcquireResourceExclusive( transactionContext->Resource ); + + if (!FlagOn(transactionContext->Flags, AV_TXCTX_LISTDRAINED)) { + + FltReferenceContext ( StreamContext ); // Q + InsertTailList( &transactionContext->ScListHead, + &StreamContext->ListInTransaction ); + } + + AvReleaseResource( transactionContext->Resource ); + + goto Cleanup; + } + + // case 2,3 + + // + // We have to query transaction outcome in order to know how we + // can process the previously outstanding transaction context. + // + + status = AvQueryTransactionOutcome( oldTxCtx->Transaction, &txOutcome ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvProcessPreviousTransaction: AvQueryTransactionOutcome FAILED!!!!\n") ); + + // + // We have exchanged the pointer anyway, if we cannot query its outcome, + // we have to go through. + // + } + + AvAcquireResourceExclusive( oldTxCtx->Resource ); + RemoveEntryList ( &StreamContext->ListInTransaction ); + AvReleaseResource( oldTxCtx->Resource ); + + AvPropagateFileState ( StreamContext, txOutcome ); + + if ( transactionContext ) { // case 3 + + FltReferenceContext( transactionContext ); + + AvAcquireResourceExclusive( transactionContext->Resource ); + + if (!FlagOn(transactionContext->Flags, AV_TXCTX_LISTDRAINED)) { + + InsertTailList( &transactionContext->ScListHead, + &StreamContext->ListInTransaction ); + + } else { + + FltReleaseContext( StreamContext ); + } + + AvReleaseResource( transactionContext->Resource ); + + } else { // case 2 + + FltReleaseContext ( StreamContext ); // Release reference count at Q + } + + // case 2,3 + + FltReleaseContext( oldTxCtx ); // Release reference count in stream context originally. + + } + + // + // We don't care about case 4, 5. + // + +Cleanup: + + if (transactionContext) { + + FltReleaseContext( transactionContext ); // Release the ref count grabbed at AvFindOrCreateTransactionContext(...) + } + + return status; +} + +FLT_POSTOP_CALLBACK_STATUS +AvPostCreate (_Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-create completion routine. + In this routine, stream context and/or transaction context shall be + created if not exits. + + Note that we only allocate and set the stream context to filter manager + at post create. + +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 completion context set in the pre-create routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status = Data->IoStatus.Status; + BOOLEAN isDir = FALSE; + BOOLEAN isTxWriter = FALSE; + + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_STREAM_CONTEXT oldStreamContext = NULL; + PAV_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + ACCESS_MASK desiredAccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess; + + BOOLEAN updateRevisionNumbers; + LONGLONG VolumeRevision, CacheRevision, FileRevision; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + if (!NT_SUCCESS( status ) || + (status == STATUS_REPARSE)) { + + // + // File Creation may fail. + // + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPostCreate: file creation failed\n") ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // After creation, skip it if it is directory. + // + + status = FltIsDirectory( FltObjects->FileObject, + FltObjects->Instance, + &isDir ); + + // + // If FltIsDirectory failed, we do not know if it is a directoy, + // we let it go through because if it is a directory, it will fail + // at section creation anyway. + // + + if ( NT_SUCCESS( status ) && isDir ) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // We skip the encrypted file open without FILE_WRITE_DATA and FILE_READ_DATA + // This is because if application calls OpenEncryptedFileRaw(...) for backup, + // it won't have to decrypt the file. In such case, if we scan it, we will hit + // an assertion error in NTFS because it does not have the encryption context. + // Thus, we have to skip the encrypted file not open for read/write. + // + + if (!(FlagOn(desiredAccess, FILE_WRITE_DATA)) && + !(FlagOn(desiredAccess, FILE_READ_DATA)) ) { + + BOOLEAN encrypted = FALSE; + status = AvGetFileEncrypted( FltObjects->Instance, + FltObjects->FileObject, + &encrypted ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPostCreate: AvGetFileEncrypted FAILED!! \n0x%x\n", status) ); + } + if (encrypted) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + } + + // + // In this sample, we skip the alternate data stream. However, you may decide + // to scan it and modify accordingly. + // + + if (AvIsStreamAlternate( Data )) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Skip a prefetch open and flag it so we skip subsequent + // IO operations on the handle. + // + + if (FlagOn((ULONG_PTR)CompletionContext, AV_FLAG_PREFETCH)) { + + if (!FltSupportsStreamHandleContexts( FltObjects->FileObject )) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + status = AvCreateStreamHandleContext( FltObjects->Filter, + &streamHandleContext ); + + if (!NT_SUCCESS(status)) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + SetFlag( streamHandleContext->Flags, AV_FLAG_PREFETCH ); + + status = FltSetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + streamHandleContext, + NULL ); + + FltReleaseContext( streamHandleContext ); + + if (!NT_SUCCESS(status)) { + + // + // Shouldn't find the handle already set + // + + ASSERT( status != STATUS_FLT_CONTEXT_ALREADY_DEFINED ); + } + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Find or create a stream context + // + + status = FltGetStreamContext( FltObjects->Instance, + FltObjects->FileObject, + &streamContext ); + + if (status == STATUS_NOT_FOUND) { + + // + // Create a stream context + // + + status = AvCreateStreamContext( FltObjects->Filter, &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to create stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FltObjects->FileObject, + FltObjects->Instance) ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Attempt to get the stream infected state from our cache + // + + status = AvGetFileId( FltObjects->Instance, FltObjects->FileObject, &streamContext->FileId ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[Av]: Failed to get file id with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FltObjects->FileObject, + FltObjects->Instance) ); + + // + // File id is optional and therefore should not affect the scan logic. + // + + AV_SET_INVALID_FILE_REFERENCE( streamContext->FileId ) + + } else { + + // + // This function will load the file infected state from the + // cache if the fileID is valid. Even if this function fails, + // we still have to move on because the cache is optional. + // + + AvLoadFileStateFromCache( FltObjects->Instance, + &streamContext->FileId, + &streamContext->State, + &streamContext->VolumeRevision, + &streamContext->CacheRevision, + &streamContext->FileRevision ); + } + + // + // Set the new context we just allocated on the file object + // + + status = FltSetStreamContext( FltObjects->Instance, + FltObjects->FileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + streamContext, + &oldStreamContext ); + + if (!NT_SUCCESS(status)) { + + if (status == STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Race: Stream context already defined. Retaining old stream context %p (FileObject = %p, Instance = %p)\n", + oldStreamContext, + FltObjects->FileObject, + FltObjects->Instance) ); + + FltReleaseContext( streamContext ); + + streamContext = oldStreamContext; + + } else { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to set stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FltObjects->FileObject, + FltObjects->Instance) ); + goto Cleanup; + } + } + + } else if (!NT_SUCCESS(status)) { + + // + // We will get here if stream contexts are not supported + // + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to get stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FltObjects->FileObject, + FltObjects->Instance) ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // If successfully opened a file with the desired access matching + // the "exclusive write" from a TxF point of view, we can guarantee that + // if previous transaction context exists, it must have been comitted + // or rollbacked. + // + + if (FlagOn( Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, + FILE_WRITE_DATA | FILE_APPEND_DATA | + DELETE | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | + WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY ) ) { + + // + // Either this file is opened in a transaction context or not, + // we need to process the previous transaction if it exists. + // AvProcessPreviousTransaction(...) handles these cases. + // + + status = AvProcessPreviousTransaction ( FltObjects, + streamContext ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPostCreate: AvProcessTransaction FAILED!! \n") ); + + goto Cleanup; + } + + isTxWriter = (FltObjects->Transaction != NULL); + } + + // + // Perform any CSVFS specific processing + // + AvPostCreateCsvfs( Data, + FltObjects, + streamContext, + &updateRevisionNumbers, + &VolumeRevision, + &CacheRevision, + &FileRevision ); + // + // Ignore return status + // + + + + if (IS_FILE_NEED_SCAN( streamContext )) { + + status = AvScan( Data, + FltObjects, + AvUserMode, + Data->Iopb->MajorFunction, + isTxWriter, + streamContext ); + if (!NT_SUCCESS( status ) || + (STATUS_TIMEOUT == status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPostCreate: AvScan FAILED!! \n") ); + + goto Cleanup; + } + } + + + // + // If needed, update the stream context with the latest revision + // numbers that correspond to the verion just scanned + // + if (updateRevisionNumbers) { + streamContext->VolumeRevision = VolumeRevision; + streamContext->CacheRevision = CacheRevision; + streamContext->FileRevision = FileRevision; + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvPostCreate: RevisionNumbers updated to %I64x:%I64x:%I64x\n", + VolumeRevision, + CacheRevision, + FileRevision) + ); + } + + if (IS_FILE_INFECTED( streamContext )) { + + // + // If the file is infected, deny the access. + // + AvCancelFileOpen(Data, FltObjects, STATUS_VIRUS_INFECTED); + + // + // If the scan timed-out or scan was failed, we let the create succeed, + // and it may cause security hole; + // + // Alternatively, you can add a state called AvFileScanFailure or equivalent, + // add a condition here and fail the create. This option will have better + // protection from viruses, but the apps will see the failures due to a + // lengthy scan or scan failure. It's a trade-off. + // + goto Cleanup; + } + +Cleanup: + + FltReleaseContext( streamContext ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + +FLT_PREOP_CALLBACK_STATUS +AvPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre-cleanup callback. Make the stream context persistent in the volatile cache. + If the file is transacted, it will be synced at KTM notification callback + if committed. + +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 - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + BOOLEAN encrypted = FALSE; + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + ULONG_PTR stackLow; + ULONG_PTR stackHigh; + + BOOLEAN updateRevisionNumbers; + LONGLONG VolumeRevision, CacheRevision, FileRevision; + + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + // + // Skip scan on prefetcher handles to avoid deadlocks + // + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &streamHandleContext ); + if (NT_SUCCESS(status)) { + + if (FlagOn( streamHandleContext->Flags, AV_FLAG_PREFETCH )) { + + // + // Because the Memory Manager can cache the file object + // and use it for other applications performing mapped I/O, + // whenever a Cleanup operation is seen on a prefetcher + // file object, that file object should no longer be + // considered prefetcher-opened. + // + + RtlInterlockedClearBits( &streamHandleContext->Flags, + AV_FLAG_PREFETCH ); + + FltDeleteStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + NULL ); + + FltReleaseContext( streamHandleContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + FltReleaseContext( streamHandleContext ); + } + + // + // Stack file objects are never scanned. + // + + IoGetStackLimits( &stackLow, &stackHigh ); + + if (((ULONG_PTR)FltObjects->FileObject > stackLow) && + ((ULONG_PTR)FltObjects->FileObject < stackHigh)) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + status = FltGetStreamContext( FltObjects->Instance, + FltObjects->FileObject, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreCleanup: find stream context failed.\n") ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // We skip encrypted files at cleanup time because we cannot be + // sure if the file is open raw for backup. It will get scanned + // on the next open anyway. + // + + status = AvGetFileEncrypted( FltObjects->Instance, + FltObjects->FileObject, + &encrypted ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPreCleanup: AvGetFileEncrypted FAILED!! \n") ); + + goto Cleanup; + } + + if (encrypted) { + + goto Cleanup; + } + + AvPreCleanupCsvfs( Data, + FltObjects, + streamContext, + &updateRevisionNumbers, + &VolumeRevision, + &CacheRevision, + &FileRevision ); + + // + // For applications, the typical calling sequence is, close the file handle + // and commit/rollback the changes. We skip the scan here for + // transacted writer because we do not know if the change will be + // rollbacked or not. If it eventually commits, it will be scanned + // at next create anyway. However, if it rollbacks, the scan here will + // be redundant. + // + + if ((streamContext->TxContext == NULL) && + IS_FILE_MODIFIED( streamContext )) { + + status = AvScan( Data, + FltObjects, + AvUserMode, + Data->Iopb->MajorFunction, + FALSE, + streamContext ); + + if (!NT_SUCCESS( status ) || STATUS_TIMEOUT == status) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvPreCleanup: AvScan FAILED!! \n") ); + + goto Cleanup; + } + + + // + // If needed, update the stream context with the latest revision + // numbers that correspond to the verion just scanned + // + if (updateRevisionNumbers) { + streamContext->VolumeRevision = VolumeRevision; + streamContext->CacheRevision = CacheRevision; + streamContext->FileRevision = FileRevision; + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvPreCleanup: RevisionNumbers updated to %I64x:%I64x:%I64x\n", + VolumeRevision, + CacheRevision, + FileRevision) + ); + } + + } + +Cleanup: + + // + // We only insert the entry when the file is clean or infected. + // + + if (!IS_FILE_MODIFIED( streamContext ) || + IS_FILE_INFECTED( streamContext )) { + + if (!NT_SUCCESS ( AvSyncCache( FltObjects->Instance, streamContext ))) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPreCleanup: AvSyncCache FAILED!! \n") ); + } + } + + FltReleaseContext( streamContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + +NTSTATUS +AvKtmNotificationCallback ( + _Unreferenced_parameter_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ) +/*++ + +Routine Description: + + The registered routine of type PFLT_TRANSACTION_NOTIFICATION_CALLBACK + in FLT_REGISTRATION structure. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + TransactionContext - Pointer to the minifilter driver's transaction context + set at PostCreate. + + TransactionNotification - Specifies the type of notifications that the + filter manager is sending to the minifilter driver. + +Return Value: + + STATUS_SUCCESS - Returning this status value indicates that the minifilter + driver is finished with the transaction. This is a success code. + + STATUS_PENDING - Returning this status value indicates that the minifilter + driver is not yet finished with the transaction. This is a success code. + +--*/ +{ + PAV_TRANSACTION_CONTEXT transactionContext = (PAV_TRANSACTION_CONTEXT) TransactionContext; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( FltObjects ); + + FLT_ASSERTMSG("[AV] AvKtmNotificationCallback: The expected type of notifications registered at FltEnlistInTransaction(...).\n", + FlagOn( TransactionNotification, + (TRANSACTION_NOTIFY_COMMIT_FINALIZE | TRANSACTION_NOTIFY_ROLLBACK) ) ); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvKtmNotificationCallback: Entered\n") ); + + if (NULL != transactionContext) { + + if ( FlagOn( TransactionNotification, TRANSACTION_NOTIFY_COMMIT_FINALIZE ) ) { + + return AvProcessTransactionOutcome( TransactionContext, TransactionOutcomeCommitted ); + + } else { + + return AvProcessTransactionOutcome( TransactionContext, TransactionOutcomeAborted ); + } + } + + return STATUS_SUCCESS; +} + +NTSTATUS +AvScanAbortCallbackAsync ( + _Unreferenced_parameter_ PFLT_INSTANCE Instance, + _In_ PFLT_CONTEXT Context, + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This routine is the registered cancel callback function in FLT_REGISTRATION. + It would be invoked by the file system if it decides to abort the scan. + As its name suggests, this function is asynchrounous, so the caller is not + blocked. + + Note: This routine may be called before FltCreateSectionForDataScan returns. + This means the SectionHandle and SectionObject may not yet be set in the + SectionContext. We can't take a dependency on these being set before needing + to abort the scan. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + Context - The section context. + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + PAV_SECTION_CONTEXT sectionCtx = (PAV_SECTION_CONTEXT) Context; + PAV_SCAN_CONTEXT scanCtx = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Instance ); + UNREFERENCED_PARAMETER( Data ); + + if (NULL == sectionCtx) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvScanAbortCallbackAsync: INVALID ARGUMENT.\n") ); + return STATUS_INVALID_PARAMETER_2; + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvScanAbortCallbackAsync: closesection handle=%p, object=%p, cancelable=%d\n", + sectionCtx->SectionHandle, + sectionCtx->SectionObject, + sectionCtx->CancelableOnConflictingIo) ); + + // + // Send abort signal only when the scanning + // happens in cancelable context (such as pre-cleanup). + // + + if (sectionCtx->CancelableOnConflictingIo) { + + // + // The only reason of scan context being NULL is that + // the section context is about to close anyway. + // Please see AvCloseSectionForDataScan(...) + // + scanCtx = InterlockedExchangePointer( §ionCtx->ScanContext, NULL ); + + if (scanCtx == NULL) { + + return STATUS_SUCCESS; + } + + sectionCtx->Aborted = TRUE; + AvSendAbortToUser( scanCtx->ScanThreadId, scanCtx->ScanId ); + + } + + return STATUS_SUCCESS; +} + +NTSTATUS +AvSetConfiguration ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Descrition: + + This routine sets the filter configuration based on registry values. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + Returns the status of this operation. + + +--*/ +{ + NTSTATUS status; + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey = NULL; + UNICODE_STRING valueName; + UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; + PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION)buffer; + ULONG valueLength = sizeof(buffer); + ULONG resultLength; + + // + // Open the SimRep registry key. + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + goto Cleanup; + } + + +#if DBG + + // + // Query the debug level + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *(PULONG)value->Data; + } + +#endif + + // + // Query the local scan timeout + // + + RtlInitUnicodeString( &valueName, L"LocalScanTimeout" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.LocalScanTimeout = (LONGLONG)(*(PULONG)value->Data); + } + + // + // Query the network scan timeout + // + + RtlInitUnicodeString( &valueName, L"NetworkScanTimeout" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.NetworkScanTimeout = (LONGLONG)(*(PULONG)value->Data); + } + + status = STATUS_SUCCESS; + +Cleanup: + + if (driverRegKey != NULL) { + + ZwClose( driverRegKey ); + } + + return status; +} + + diff --git a/filesys/miniFilter/avscan/filter/avscan.h b/filesys/miniFilter/avscan/filter/avscan.h new file mode 100644 index 00000000..45c9ba3e --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.h @@ -0,0 +1,271 @@ +/*++ + +Copyright (c) 1989-2011 Microsoft Corporation + +Module Name: + + avscan.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. Mainly used by avscan module. + +Environment: + + Kernel mode + +--*/ +#ifndef __AVSCAN_H__ +#define __AVSCAN_H__ + +#ifndef RTL_USE_AVL_TABLES +#define RTL_USE_AVL_TABLES +#endif // RTL_USE_AVL_TABLES + +#define AV_VISTA (NTDDI_VERSION >= NTDDI_VISTA) + +#include <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> +#include "utility.h" +#include "context.h" +#include "scan.h" +#include "csvfs.h" +#include "avlib.h" + + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +// +// Scan context. +// +// We chose to seperate scan context and section context to have one struct per concept. +// The I/O request thread does not need to know how scanner implement the scan, so +// that the I/O request thread has less coupling with scanner threads. +// +// You can also put all of fields of the scan context into a section context, and allocate +// section context at the place of allocation of scan context. +// + +typedef struct _AV_SCAN_CONTEXT { + + LONG RefCount; + PFLT_INSTANCE FilterInstance; + PFILE_OBJECT FileObject; + KEVENT ScanCompleteNotification; + LIST_ENTRY List; + PAV_SECTION_CONTEXT SectionContext; + LONGLONG ScanId; + ULONG ScanThreadId; + + UCHAR IOMajorFunctionAtScan; + BOOLEAN IsFileInTxWriter; + BOOLEAN IoWaitOnScanCompleteNotificationAborted; + +} AV_SCAN_CONTEXT, *PAV_SCAN_CONTEXT; + +// +// The global variable +// + +typedef struct _AV_SCANNER_GLOBAL_DATA { + + // + // A counter for Scan Id + // + + LONGLONG ScanIdCounter; + + // + // The global FLT_FILTER pointer. Many API needs this, such as + // FltAllocateContext(...) + // + + PFLT_FILTER Filter; + + // + // Server-side communicate ports. + // + + PFLT_PORT ScanServerPort; + PFLT_PORT AbortServerPort; + PFLT_PORT QueryServerPort; + + // + // The scan client ports. + // These ports are assigned at AvConnectNotifyCallback and cleaned at AvDisconnectNotifyCallback + // + // ScanClientPort is the connection port regarding the scan message. + // AbortClientPort is the connection port regarding the abort message. + // QueryClient is the connection port regarding the query command. + // + + PFLT_PORT ScanClientPort; + PFLT_PORT AbortClientPort; + PFLT_PORT QueryClientPort; + + // + // Scan context list head. + // At AvMessageNotifyCallback, when user passes ScanCtxId, we + // have to check the validity of the id by checking this list. + // + + LIST_ENTRY ScanCtxListHead; + + // + // The lock that synchronizes the accesses of the scan context list above. + // + + ERESOURCE ScanCtxListLock; + + // + // Timeout for local file scans in milliseconds + // + + LONGLONG LocalScanTimeout; + + // + // Timeout for network file scans in milliseconds + // + + LONGLONG NetworkScanTimeout; + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + + // + // A flag that indicating that the filter is being unloaded. + // + + BOOLEAN Unloading; + +} AV_SCANNER_GLOBAL_DATA, *PAV_SCANNER_GLOBAL_DATA; + +AV_SCANNER_GLOBAL_DATA Globals; + +#if DBG + +// +// Debugging level flags. +// + +#define AVDBG_TRACE_ROUTINES 0x00000001 +#define AVDBG_TRACE_OPERATION_STATUS 0x00000002 +#define AVDBG_TRACE_DEBUG 0x00000004 +#define AVDBG_TRACE_ERROR 0x00000008 + +#define AV_DBG_PRINT( _dbgLevel, _string ) \ + if(FlagOn(Globals.DebugLevel,(_dbgLevel))) { \ + DbgPrint _string; \ + } + +#else + +#define AV_DBG_PRINT(_dbgLevel, _string) {NOTHING;} + +#endif + +FORCEINLINE +VOID +AvCancelFileOpen( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ NTSTATUS Status + ) +/*++ + +Routine Description: + + This function cancel the file open. This is supposed to be called at post create if + the I/O is cancelled. + +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. + + Status - The status code to be returned for this IRP. + +Return Value: + + None. + +--*/ + +{ + FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject ); + Data->IoStatus.Status = Status; + Data->IoStatus.Information = 0; +} + + +NTSTATUS +AvPrepareServerPort( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ AVSCAN_CONNECTION_TYPE ConnectionType + ); + +NTSTATUS +AvSendAbortToUser ( + _In_ ULONG ScanThreadId, + _In_ LONGLONG ScanId + ); + +NTSTATUS +AvAllocateScanContext( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Outptr_ PAV_SCAN_CONTEXT *ScanContext + ); + +NTSTATUS +AvReferenceScanContext( + _In_ PAV_SCAN_CONTEXT ScanContext + ); + +NTSTATUS +AvReleaseScanContext( + _In_ PAV_SCAN_CONTEXT ScanContext + ); + +// +// Fianlize function for scan context and section context. +// Wrapper functions of synchronization calling sequences. +// In the normal cases, the caller should call AvFinalizeScanAndSection +// when it finishes using it. +// +// Unless the caller wants to do things about section context inside scan context, +// then it should call AvFinalizeScanContext(), and followed by +// AvFinalizeSectionContext() +// +// These wrappers are designed to make the synchronization easier. +// +NTSTATUS +AvFinalizeScanAndSection ( + _Inout_ PAV_SCAN_CONTEXT ScanContext + ); + +NTSTATUS +AvFinalizeSectionContext ( + _Inout_ PAV_SECTION_CONTEXT SectionContext + ); + +VOID +AvFinalizeScanContext ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _Outptr_result_maybenull_ PAV_SECTION_CONTEXT *SectionContext + ); + +#endif + diff --git a/filesys/miniFilter/avscan/filter/avscan.rc b/filesys/miniFilter/avscan/filter/avscan.rc new file mode 100644 index 00000000..db19497a --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Anti-virus Filter Driver" +#define VER_INTERNALNAME_STR "avscan.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/avscan/filter/avscan.vcxproj b/filesys/miniFilter/avscan/filter/avscan.vcxproj new file mode 100644 index 00000000..d68461a9 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.vcxproj @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{9D7DE7C5-51FC-4465-B1F9-0B3C9900477A}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{94FDDA0B-6FFA-4B09-A8F0-A303F46D8BF0}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>avscan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>avscan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>avscan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>avscan</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="avscan.c" /> + <ClCompile Include="communication.c" /> + <ClCompile Include="context.c" /> + <ClCompile Include="csvfs.c" /> + <ClCompile Include="scan.c" /> + <ClCompile Include="utility.c" /> + <ResourceCompile Include="avscan.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters b/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters new file mode 100644 index 00000000..56c60b39 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{6A96A213-242A-44D1-87C3-9EF1E57E7EC9}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{794C3035-6983-4AF3-A981-5EEAA51276B3}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{A7F78AF9-20F5-4D0E-83A9-8E19405F5C0C}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{0D596D83-E754-4E17-BD2E-884C8CCFCF7D}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="avscan.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="communication.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="context.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="csvfs.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="scan.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="utility.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="avscan.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/avscan/filter/communication.c b/filesys/miniFilter/avscan/filter/communication.c new file mode 100644 index 00000000..ab3b00dd --- /dev/null +++ b/filesys/miniFilter/avscan/filter/communication.c @@ -0,0 +1,1407 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + communication.c + +Abstract: + + Communication module implementation. + This module contains the routines that involves the communication + between kernel mode and user mode. + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" + +NTSTATUS +AvConnectNotifyCallback ( + _In_ PFLT_PORT ClientPort, + _In_ PVOID ServerPortCookie, + _In_reads_bytes_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Outptr_result_maybenull_ PVOID *ConnectionCookie + ); + +VOID +AvDisconnectNotifyCallback( + _In_opt_ PVOID ConnectionCookie + ); + +NTSTATUS +AvMessageNotifyCallback ( + _In_ PVOID ConnectionCookie, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ ULONG InputBufferSize, + _Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferSize, + _Out_ PULONG ReturnOutputBufferLength + ); + +// +// Local routines +// + +NTSTATUS +AvGetScanCtxSynchronized ( + _In_ LONGLONG ScanId, + _Out_ PAV_SCAN_CONTEXT *ScanCtx + ); + +NTSTATUS +AvGetInstanceContextByVolume ( + _In_ PFLT_VOLUME volumeObject, + _Out_ PAV_INSTANCE_CONTEXT *InstanceContext + ); + +NTSTATUS +AvGetInstanceContextByFileHandle ( + _In_ HANDLE Handle, + _Out_ PAV_INSTANCE_CONTEXT *InstanceContext + ); + +NTSTATUS +AvGetStreamContextByHandle ( + _In_ HANDLE Handle, + _Out_ PAV_STREAM_CONTEXT *StreamContext + ); + +NTSTATUS +AvUpdateStreamContextWithScanResult ( + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _In_ PAV_SCAN_CONTEXT ScanContext, + _In_ AVSCAN_RESULT ScanResult + ); + +NTSTATUS +AvHandleCmdCreateSectionForDataScan ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _Out_ PHANDLE SectionHandle + ); + +NTSTATUS +AvHandleCmdCloseSectionForDataScan ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _In_ AVSCAN_RESULT ScanResult + ); + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, AvMessageNotifyCallback) + #pragma alloc_text(PAGE, AvConnectNotifyCallback) + #pragma alloc_text(PAGE, AvDisconnectNotifyCallback) + #pragma alloc_text(PAGE, AvPrepareServerPort) + + #pragma alloc_text(PAGE, AvGetInstanceContextByVolume) + #pragma alloc_text(PAGE, AvGetInstanceContextByFileHandle) + #pragma alloc_text(PAGE, AvGetStreamContextByHandle) + #pragma alloc_text(PAGE, AvUpdateStreamContextWithScanResult) + #pragma alloc_text(PAGE, AvFinalizeScanAndSection) + #pragma alloc_text(PAGE, AvFinalizeScanContext) + #pragma alloc_text(PAGE, AvFinalizeSectionContext) + #pragma alloc_text(PAGE, AvHandleCmdCreateSectionForDataScan) + #pragma alloc_text(PAGE, AvHandleCmdCloseSectionForDataScan) +#endif + +NTSTATUS +AvConnectNotifyCallback ( + _In_ PFLT_PORT ClientPort, + _In_ PVOID ServerPortCookie, + _In_reads_bytes_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Outptr_result_maybenull_ PVOID *ConnectionCookie + ) +/*++ + +Routine Description + + Communication connection callback routine. + This is called when user-mode connects to the server port. + +Arguments + + ClientPort - This is the client connection port that will be used to send messages from the filter + + ServerPortCookie - Unused + + ConnectionContext - The connection context passed from the user. This is to recognize which type + connection the user is trying to connect. + + SizeofContext - The size of the connection context. + + ConnectionCookie - Propagation of the connection context to disconnection callback. + +Return Value + + STATUS_SUCCESS - to accept the connection + STATUS_INSUFFICIENT_RESOURCES - if memory is not enough + STATUS_INVALID_PARAMETER_3 - Connection context is not valid. +--*/ +{ + PAV_CONNECTION_CONTEXT connectionCtx = (PAV_CONNECTION_CONTEXT) ConnectionContext; + PAVSCAN_CONNECTION_TYPE connectionCookie = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ServerPortCookie ); + UNREFERENCED_PARAMETER( SizeOfContext ); + + if (NULL == connectionCtx) { + + return STATUS_INVALID_PARAMETER_3; + } + + // + // ConnectionContext passed in may be deleted. We need to make a copy of it. + // + + connectionCookie = ExAllocatePoolWithTag( PagedPool, + sizeof(AVSCAN_CONNECTION_TYPE), + AV_CONNECTION_CTX_TAG ); + if (NULL == connectionCookie) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + *connectionCookie = connectionCtx->Type; + switch (connectionCtx->Type) { + case AvConnectForScan: + Globals.ScanClientPort = ClientPort; + *ConnectionCookie = connectionCookie; + break; + case AvConnectForAbort: + Globals.AbortClientPort = ClientPort; + *ConnectionCookie = connectionCookie; + break; + case AvConnectForQuery: + Globals.QueryClientPort = ClientPort; + *ConnectionCookie = connectionCookie; + break; + default: + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: AvConnectNotifyCallback: No such connection type. \n") ); + ExFreePoolWithTag( connectionCookie, + AV_CONNECTION_CTX_TAG ); + *ConnectionCookie = NULL; + return STATUS_INVALID_PARAMETER_3; + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV]: AvConnectNotifyCallback entered. type: %d \n", connectionCtx->Type) ); + + return STATUS_SUCCESS; +} + +VOID +AvDisconnectNotifyCallback( + _In_opt_ PVOID ConnectionCookie + ) +/*++ + +Routine Description + + Communication disconnection callback routine. + This is called when user-mode disconnects the server port. + +Arguments + + ConnectionCookie - The cookie set in AvConnectNotifyCallback(...). It is connection context. + +Return Value + + None +--*/ +{ + PAVSCAN_CONNECTION_TYPE connectionType = (PAVSCAN_CONNECTION_TYPE) ConnectionCookie; + + PAGED_CODE(); + + if (NULL == connectionType) { + + return; + } + // + // Close communication handle + // + switch (*connectionType) { + case AvConnectForScan: + FltCloseClientPort( Globals.Filter, &Globals.ScanClientPort ); + Globals.ScanClientPort = NULL; + break; + case AvConnectForAbort: + FltCloseClientPort( Globals.Filter, &Globals.AbortClientPort ); + Globals.AbortClientPort = NULL; + break; + case AvConnectForQuery: + FltCloseClientPort( Globals.Filter, &Globals.QueryClientPort ); + Globals.QueryClientPort = NULL; + break; + default: + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: AvDisconnectNotifyCallback: No such connection type. \n") ); + return; + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV]: AvDisconnectNotifyCallback entered. type: %d \n", *connectionType) ); + + ExFreePoolWithTag( connectionType, + AV_CONNECTION_CTX_TAG ); + +} + +NTSTATUS +AvGetScanCtxSynchronized ( + _In_ LONGLONG ScanId, + _Out_ PAV_SCAN_CONTEXT *ScanCtx + ) +/*++ + +Routine Description + + A helper function to retrieve the scan context from its scan context id. + It is synchronized by a lock. + +Arguments + + ScanId - The scan id to be found. + ScanCtx - The output scan context. NULL if not found + +Return Value + + STATUS_SUCCESS - if found. + Otherwise - Error, or if not found. + +--*/ +{ + PLIST_ENTRY link; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN found = FALSE; + PAV_SCAN_CONTEXT scanCtx = NULL; + + // + // We only 'read' the scan context when we traversing the list + // + + AvAcquireResourceShared( &Globals.ScanCtxListLock ); + + for (link = Globals.ScanCtxListHead.Flink; + link != &Globals.ScanCtxListHead; + link = link->Flink) { + + scanCtx = CONTAINING_RECORD( link, AV_SCAN_CONTEXT, List ); + + if (scanCtx->ScanId == ScanId) { + found = TRUE; + AvReferenceScanContext( scanCtx ); + break; + } + + } + + AvReleaseResource( &Globals.ScanCtxListLock ); + + if (found) { + + *ScanCtx = scanCtx; + return STATUS_SUCCESS; + } + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetScanCtxSynchronized: scan context not found. \n") ); + + *ScanCtx = NULL; + + if (NT_SUCCESS( status )){ + + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + +NTSTATUS +AvGetInstanceContextByVolume ( + _In_ PFLT_VOLUME VolumeObject, + _Out_ PAV_INSTANCE_CONTEXT *InstanceContext + ) +/*++ + +Routine Description + + A helper function to retrieve the instance context from its volume object. + + The caller is responsible for dereference InstanceContext via calling + FltReleaseContext(...) if success. + +Arguments + + VolumeObject - The volume object. + InstanceContext - The output instance context. NULL if not found + +Return Value + + STATUS_SUCCESS - if found. + Otherwise - Error, or not found. +--*/ +{ + ULONG i; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN found = FALSE; + PFLT_INSTANCE *instArray = NULL; + ULONG instCnt = 0; + PAV_INSTANCE_CONTEXT instCtx = NULL; + + PAGED_CODE(); + + status = AvEnumerateInstances ( &instArray, &instCnt ); + + if ( !NT_SUCCESS(status) ) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByVolume: Failed to enumerate instances. \n") ); + return status; + } + + for (i = 0; i < instCnt; i++) { + + status = FltGetInstanceContext( instArray[i], &instCtx ); + + if ( !NT_SUCCESS(status) ) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByVolume: Failed to get instance context. \n") ); + break; + } + + if (instCtx->Volume == VolumeObject) { + + // + // When found, we do not release the reference of instance context + // because the caller is responsible for releasing it. + // + + found = TRUE; + break; + } + + FltReleaseContext( instCtx ); + } + + AvFreeInstances( instArray, instCnt ); + instArray = NULL; + + if (found) { + + *InstanceContext = instCtx; + return STATUS_SUCCESS; + } + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByVolume: instance context not found. \n") ); + + if ( NT_SUCCESS( status ) ){ + + status = STATUS_UNSUCCESSFUL; + } + + instCtx = NULL; + + return status; +} + +NTSTATUS +AvGetInstanceContextByFileHandle ( + _In_ HANDLE Handle, + _Out_ PAV_INSTANCE_CONTEXT *InstanceContext + ) +/*++ + +Routine Description + + A helper function to retrieve the instance context from file handle. + + The caller is responsible for dereference InstanceContext via calling + FltReleaseContext(...) if success. + +Arguments + + Handle - The file handle of interest. + InstanceContext - The output instance context. NULL if not found + +Return Value + + STATUS_SUCCESS - if found. + Otherwise - if not found; it will report the corresponding status. +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PFILE_OBJECT fileObject = NULL; + PFLT_VOLUME volumeObject = NULL; + + PAGED_CODE(); + + // + // Get file object by handle + // + + status = ObReferenceObjectByHandle ( + Handle, + 0, + *IoFileObjectType, + KernelMode, + (PVOID *)&fileObject, + NULL + ); + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByFileHandle: Failed to get file object by handle. \n") ); + return status; + } + + try { + + status = FltGetVolumeFromFileObject( Globals.Filter, + fileObject, + &volumeObject ); + + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetInstanceContextByFileHandle: Failed to get volume by file object. \n") ); + leave; + } + + status = AvGetInstanceContextByVolume(volumeObject, InstanceContext); + + FltObjectDereference( volumeObject ); + + } finally { + + ObDereferenceObject( fileObject ); + } + + return status; +} + +NTSTATUS +AvGetStreamContextByHandle ( + _In_ HANDLE Handle, + _Out_ PAV_STREAM_CONTEXT *StreamContext + ) +/*++ + +Routine Description + + A helper function to retrieve the stream context from a file handle at message + callback routine. This function will increment the reference count of the + output stream context. + + The caller is responsible for dereference it via calling FltReleaseContext(...) + if success. + +Arguments + + Handle - The file handle of interest. + StreamContext - The output stream context. NULL if not found + +Return Value + + STATUS_SUCCESS - if found. + Otherwise - if not found; it will report the corresponding status. +--*/ +{ + NTSTATUS status; + PFILE_OBJECT fileObject = NULL; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + status = AvGetInstanceContextByFileHandle( Handle, &instanceContext); + + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: ***AvGetInstanceContextByFileHandle FAILED. \n") ); + return status; + } + try { + + status = ObReferenceObjectByHandle ( + Handle, + 0, + *IoFileObjectType, + KernelMode, + (PVOID *)&fileObject, + NULL + ); + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvGetStreamContextByHandle: Failed to get file object by handle. \n") ); + leave; + } + + status = FltGetStreamContext( instanceContext->Instance, + fileObject, + StreamContext ); + + ObDereferenceObject( fileObject ); + + } finally { + + FltReleaseContext( instanceContext ); + } + return status; +} + +NTSTATUS +AvHandleCmdCreateSectionForDataScan ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _Out_ PHANDLE SectionHandle + ) +/*++ + +Routine Description: + + This function handles CmdCreateSectionForDataScan message. + This function will create and return the section handle to the caller. + If any error occurs, it will trigger events to release the waiting threads. + + NOTE: this function does not check the buffer size etc. + It must be checked before passing into this function. + +Arguments: + + ScanContext - The scan context. + ScanThreadId - The thread ID of the thread doing the scan + SectionHandle - receives the section handle + +Return Value: + + Returns the status of processing the message. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_STREAM_CONTEXT streamContext = NULL; + PAV_SECTION_CONTEXT sectionContext = NULL; + HANDLE sectionHandle = NULL; + + PAGED_CODE(); + + status = FltGetStreamContext ( ScanContext->FilterInstance, + ScanContext->FileObject, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvHandleCmdCreateSectionForDataScan: failed to get stream context.\n") ); + + goto Cleanup; + } + + // + // It should be impossible for the stream state to change from + // uknown to clean since we kicked off this scan. + // + + ASSERT(IS_FILE_NEED_SCAN( streamContext )); + + status = AvCreateSectionContext( ScanContext->FilterInstance, + ScanContext->FileObject, + §ionContext); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvHandleCmdCreateSectionForDataScan: failed to create section context.\n") ); + + goto Cleanup; + } + + // + // Before we are going to create section object, if this flag is set (by the thread that requests for scan), + // it means that the thread is trying to cancel this scan, and thus we don't want the scan to proceed anymore. + // + if (ScanContext->IoWaitOnScanCompleteNotificationAborted) { + + status = STATUS_CANCELLED; + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvHandleCmdCreateSectionForDataScan: Before FltCreateSectionForDataScan, it found Io is trying to abort the wait.\n") ); + + goto Cleanup; + } + + sectionContext->ScanContext = ScanContext; + sectionContext->CancelableOnConflictingIo = (ScanContext->IOMajorFunctionAtScan == IRP_MJ_CLEANUP); + + // + // Note: the section conflict callback could be called before + // this routine returns. It is even possible that the context + // passed to the callback won't have the SectionHandle and + // SectionObject fields set yet. + // + + status = FltCreateSectionForDataScan( ScanContext->FilterInstance, + ScanContext->FileObject, + sectionContext, + SECTION_MAP_READ, + NULL, + NULL, + PAGE_READONLY, + SEC_COMMIT, + 0, + §ionContext->SectionHandle, + §ionContext->SectionObject, + NULL ); + + sectionHandle = sectionContext->SectionHandle; + + if (!NT_SUCCESS( status )) { + +#if DBG + NTSTATUS sta = STATUS_SUCCESS; + PFLT_VOLUME volumeObject = NULL; + ULONG length = 0; + UCHAR volPropBuffer[sizeof(FLT_VOLUME_PROPERTIES)+256]; //enough space for names + PFLT_VOLUME_PROPERTIES property = (PFLT_VOLUME_PROPERTIES)volPropBuffer; + + sta = FltGetVolumeFromFileObject( Globals.Filter, + ScanContext->FileObject, + &volumeObject ); + if (NT_SUCCESS( sta )) { + sta = FltGetVolumeProperties( volumeObject, + property, + sizeof(volPropBuffer), + &length ); + if (NT_SUCCESS( sta )) { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] ############## %wZ, %wZ, %wZ\n", + property->FileSystemDriverName, + property->FileSystemDeviceName, + property->RealDeviceName) ); + } + FltObjectDereference( volumeObject ); + } + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvHandleCmdCreateSectionForDataScan: %I64x,%I64x failed to create section object. 0x%x\n", + streamContext->FileId.FileId64.UpperZeroes, + streamContext->FileId.FileId64.Value, + sta) ); +#endif // DBG + + goto Cleanup; + } + + // + // Before scanning, we set the file status as scanning. + // This is important when another thread is writing to this file while we are scanning + // this file. + // + + SET_FILE_SCANNING_EX( ScanContext->IsFileInTxWriter, streamContext ); + + // + // Only after the section object is successfully created, we put a section context pointer + // into the scan context. + // + + FltReferenceContext( sectionContext ); + ScanContext->SectionContext = sectionContext; + + *SectionHandle = sectionHandle; + +Cleanup: + + // + // The I/O request thread is waiting for this event. + // If any error occurs, we have to release the waiting thread. + // if status is a success code, the thread will get released when + // the user send message to close the section object. + // + + if (!NT_SUCCESS( status )) { + + KeSetEvent( &ScanContext->ScanCompleteNotification, 0, FALSE ); + } + + if (streamContext) { + + // + // On error signal the event to release any threads waiting to + // scan the same file. On success it will get released when the + // message is sent to close the section object. + // + + if (!NT_SUCCESS( status )) { + + SET_FILE_MODIFIED_EX( ScanContext->IsFileInTxWriter, streamContext ); + } + + FltReleaseContext( streamContext ); + streamContext = NULL; + } + + if (sectionContext) { + + FltReleaseContext( sectionContext ); + sectionContext = NULL; + } + + // + // After this routine assigned section context into scan context and created sectionHandle, + // we need to check if notification abort flag was set, if it was, we need to fail the section creation + // because at this point, the request for scan was abondaned anyway, we 'stop' the scan by + // returning STATUS_CANCELLED to the user. + // + if (NT_SUCCESS( status ) && + ScanContext->IoWaitOnScanCompleteNotificationAborted) { + + status = STATUS_CANCELLED; + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvHandleCmdCreateSectionForDataScan: After FltCreateSectionForDataScan, it found Io is trying to abort the wait.\n") ); + + // + // We explicitly call NtClose() instead of ZwClose() so that PreviousMode() will be User. + // This prevents accidental closing of a kernel handle and also will not bugcheck the + // system if the handle value is no longer valid + // + NtClose( sectionHandle ); + + // + // This user mode handle is supposed to be closed in the user mode program. + // We close in the context of the same process context. + // + AvFinalizeScanAndSection( ScanContext ); + } + + return status; +} + + +NTSTATUS +AvUpdateStreamContextWithScanResult ( + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _In_ PAV_SCAN_CONTEXT ScanContext, + _In_ AVSCAN_RESULT ScanResult + ) +/*++ + +Routine Description: + + This function updates StreamContex according to ScanResult. + e.g. Set the stream as modified, infected, etc. + +Arguments: + + StreamContext - The stream context to be updated. + + ScanContext - The scan context. + + ScanResult - The scan result. Please see the definition of AVSCAN_RESULT. + +Return Value: + + Returns STATUS_SUCCESS. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + switch( ScanResult ) { + + case AvScanResultUndetermined: + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("***[AV] AvUpdateScanResult: the caller did not specify the scan result.\n") ); + // + // If for some reason, the scan result returns undetermined, we have to + // set the file state back to AvFileModifed. + // + + SET_FILE_MODIFIED_EX( ScanContext->IsFileInTxWriter, StreamContext); + break; + case AvScanResultInfected: + // + // If after the scan and before setting this file as clean, the file gets modified, + // then we have to leave it as modified. + // + + if (ScanContext->IsFileInTxWriter) { + + InterlockedCompareExchange( &StreamContext->TxState, AvFileInfected, AvFileScanning ); + + } else { + + InterlockedCompareExchange( &StreamContext->State, AvFileInfected, AvFileScanning ); + } + break; + case AvScanResultClean: + + // + // If after the scan and before setting this file as clean, the file gets modified, + // then we have to leave it as modified. + // + + if (ScanContext->IsFileInTxWriter) { + + InterlockedCompareExchange( &StreamContext->TxState, AvFileNotInfected, AvFileScanning ); + + } else { + + InterlockedCompareExchange( &StreamContext->State, AvFileNotInfected, AvFileScanning ); + } + + break; + default: + FLT_ASSERTMSG( "No such scan result.\n", FALSE); + break; + } + + return status; +} + +NTSTATUS +AvFinalizeScanAndSection ( + _Inout_ PAV_SCAN_CONTEXT ScanContext + ) +/*++ + +Routine Description: + + This function is a wrapper function to finalize scan context and section context. + Normally, you should call this function if you don't need to use section context before + closing it. + +Arguments: + + ScanContext - The scan context. + +Return Value: + + Returns the status code from FltCloseSectionForDataScan. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_SECTION_CONTEXT sectionContext = NULL; + + PAGED_CODE(); + + AvFinalizeScanContext( ScanContext, §ionContext ); + + // + // This thread won the race, and is responsible for finalizing the section context + // + if (sectionContext != NULL) { + + status = AvFinalizeSectionContext( sectionContext ); + } + return status; +} + +VOID +AvFinalizeScanContext ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _Outptr_result_maybenull_ PAV_SECTION_CONTEXT *SectionContext + ) +/*++ + +Routine Description: + + This function interlocked-exchange the section context inside the scan context and + release the waiting I/O request thread. + + The caller is responsible for releasing the reference count of SectionContext + when it successfully exchanges a non-NULL section context. + +Arguments: + + ScanContext - The scan context. + + SectionContext - Receives the sectioncontext address indicating the caller is + responsible for tearing down the sectioncontext. + Receives NULL if the context is already being torn down by another thread. + +Return Value: + + None. + +--*/ +{ + PAV_SECTION_CONTEXT oldSectionCtx = NULL; + + PAGED_CODE(); + + *SectionContext = NULL; + + // + // Synchronization between AvInstanceTeardownStart(...) or timeout + // processing in the IO thread. + // + + oldSectionCtx = InterlockedExchangePointer( &ScanContext->SectionContext, NULL ); + + // + // If sectionContext is NULL, it means that another thread has + // already begun teardown of the section. + // + + if (oldSectionCtx) { + + // + // The caller is responsible for releasing the reference count when assigned in ScanContext. + // + *SectionContext = oldSectionCtx; + } + + // + // The I/O request thread is waiting for this event. + // + + KeSetEvent( &ScanContext->ScanCompleteNotification, 0, FALSE ); +} + +NTSTATUS +AvFinalizeSectionContext ( + _Inout_ PAV_SECTION_CONTEXT SectionContext + ) +/*++ + +Routine Description: + + This function is a wrapper function to finalize section context. + It closes the section context/object and release its reference. + +Arguments: + + SectionContext - The section context. + +Return Value: + + Returns the status code from FltCloseSectionForDataScan. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + status = AvCloseSectionForDataScan( SectionContext ); + + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("***[AV]: AvFinalizeSectionContext: Close section failed.\n") ); + } + FltReleaseContext( SectionContext ); + return status; +} + +NTSTATUS +AvHandleCmdCloseSectionForDataScan ( + _Inout_ PAV_SCAN_CONTEXT ScanContext, + _In_ AVSCAN_RESULT ScanResult + ) +/*++ + +Routine Description: + + This function handles AvCmdCloseSectionForDataScan message. + This function will + + 1) close the section object + 2) Set the file clean or infected. + 3) trigger events to release the waiting threads. + +Arguments: + + ScanContext - The scan context. + + ScanResult - The scan result. Please see the definition of AVSCAN_RESULT. + +Return Value: + + Returns the status of processing the message. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_STREAM_CONTEXT streamContext = NULL; + + PAGED_CODE(); + + status = FltGetStreamContext ( ScanContext->FilterInstance, + ScanContext->FileObject, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("***[AV] AvHandleCmdCloseSectionForDataScan: failed to get stream context.\n") ); + + goto Cleanup; + } + + // + // Update stream context will succeed. + // + AvUpdateStreamContextWithScanResult(streamContext, ScanContext, ScanResult); + +Cleanup: + + status = AvFinalizeScanAndSection( ScanContext ); + + // + // Either the above operations are successful, or any error occur, + // we have to release the stream context. + // + + if ( streamContext ) { + + FltReleaseContext( streamContext ); + } + + return status; + +} + +NTSTATUS +AvMessageNotifyCallback ( + _In_ PVOID ConnectionCookie, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ ULONG InputBufferSize, + _Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferSize, + _Out_ PULONG ReturnOutputBufferLength + ) +/*++ + +Routine Description: + + This routine is called whenever the user program sends message to + filter via FilterSendMessage(...). + + The user space scanner sends message to + + 1) Create the section for data scan + 2) Close the section for data scan + 3) Set a certain file to be infected + 4) Query the file state of a file + +Arguments: + + InputBuffer - A buffer containing input data, can be NULL if there + is no input data. + + InputBufferSize - The size in bytes of the InputBuffer. + + OutputBuffer - A buffer provided by the application that originated + the communication in which to store data to be returned to the + application. + + OutputBufferSize - The size in bytes of the OutputBuffer. + + ReturnOutputBufferSize - The size in bytes of meaningful data + returned in the OutputBuffer. + +Return Value: + + Returns the status of processing the message. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + AVSCAN_COMMAND command; + HANDLE hFile = NULL; + LONGLONG scanId = 0; + PAV_SCAN_CONTEXT scanContext = NULL; + AVSCAN_RESULT scanResult = AvScanResultUndetermined; + PAV_STREAM_CONTEXT streamContext; + HANDLE sectionHandle; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ConnectionCookie ); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV]: AvMessageNotifyCallback entered. \n") ); + + + if ((InputBuffer == NULL) || + (InputBufferSize < (FIELD_OFFSET(COMMAND_MESSAGE, Command) + + sizeof(AVSCAN_COMMAND)))) { + + return STATUS_INVALID_PARAMETER; + } + + try { + + // + // Probe and capture input message: the message is raw user mode + // buffer, so need to protect with exception handler + // + + command = ((PCOMMAND_MESSAGE) InputBuffer)->Command; + scanId = ((PCOMMAND_MESSAGE) InputBuffer)->ScanId; + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + // + // Only + // AvCmdCreateSectionForDataScan + // AvCmdCloseSectionForDataScan + // require the check of scanCtxId + // + // We also check the output buffer size, and its alignment here. + // + + switch (command) { + + case AvCmdCreateSectionForDataScan: + + if ((OutputBufferSize < sizeof (HANDLE)) || + (OutputBuffer == NULL)) { + + return STATUS_INVALID_PARAMETER; + } + + if (!IS_ALIGNED(OutputBuffer,sizeof(HANDLE))) { + + return STATUS_DATATYPE_MISALIGNMENT; + } + + status = AvGetScanCtxSynchronized( scanId, + &scanContext ); + + if (!NT_SUCCESS( status )) { + + return STATUS_NOT_FOUND; + } + + status = AvHandleCmdCreateSectionForDataScan( scanContext, + §ionHandle ); + + if (NT_SUCCESS(status)) { + // + // We succesfully created a section object/handle. + // Try to set the handle in the OutputBuffer + // + try { + + (*(PHANDLE)OutputBuffer) = sectionHandle; + *ReturnOutputBufferLength = sizeof(HANDLE); + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + // + // We cannot depend on user service program to close this handle for us. + // We explicitly call NtClose() instead of ZwClose() so that PreviousMode() will be User. + // This prevents accidental closing of a kernel handle and also will not bugcheck the + // system if the handle value is no longer valid + // + NtClose( sectionHandle ); + + // + // Close section and release the waiting I/O request thread + // We treat invalid user buffer as an exception and remove + // section object inside scan context. You can also design a protocol + // that have user program to re-try for section creation failure. + // + AvFinalizeScanAndSection( scanContext ); + status = GetExceptionCode(); + } + } + + // + // AvGetScanCtxSynchronized incremented the ref count of scan context + // + AvReleaseScanContext( scanContext ); + + break; + + case AvCmdCloseSectionForDataScan: + + try { + + scanResult = ((PCOMMAND_MESSAGE) InputBuffer)->ScanResult; + + if (scanResult == AvScanResultInfected) { + AV_DBG_PRINT( AVDBG_TRACE_OPERATION_STATUS, + ("[AV]: *******AvCmdCreateSectionForDataScan FAILED. \n") ); + } + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + status = AvGetScanCtxSynchronized( scanId, + &scanContext ); + + if (!NT_SUCCESS( status )) { + + return STATUS_NOT_FOUND; + } + + status = AvHandleCmdCloseSectionForDataScan( scanContext, scanResult ); + + if (NT_SUCCESS(status)) { + *ReturnOutputBufferLength = 0; + } + + // + // AvGetScanCtxSynchronized incremented the ref count of scan context + // + AvReleaseScanContext( scanContext ); + + break; + + case AvIsFileModified: + + try { + + hFile = ((PCOMMAND_MESSAGE) InputBuffer)->FileHandle; + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + if ((OutputBufferSize < sizeof (BOOLEAN)) || + (OutputBuffer == NULL)) { + + return STATUS_INVALID_PARAMETER; + } + + if (!IS_ALIGNED(OutputBuffer,sizeof(BOOLEAN))) { + + return STATUS_DATATYPE_MISALIGNMENT; + } + + // + // Get file object by file handle + // Get PFLT_VOLUME by file object + // Get instance context by PFLT_VOLUME + // Get filter instance in instance context + // Get stream context by file object and instance context + // Return if the file was previously modified + // + + status = AvGetStreamContextByHandle( hFile, &streamContext ); + + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: **************************AvGetStreamContextByHandle FAILED. \n") ); + break; + } + + try { + + (*(PBOOLEAN) OutputBuffer) = (BOOLEAN) IS_FILE_MODIFIED( streamContext ); + *ReturnOutputBufferLength = (ULONG) sizeof( BOOLEAN ); + + } except (AvExceptionFilter( GetExceptionInformation(), TRUE )) { + + status = GetExceptionCode(); + } + + FltReleaseContext( streamContext ); + + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + return status; + +} + +NTSTATUS +AvPrepareServerPort( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ AVSCAN_CONNECTION_TYPE ConnectionType + ) +/*++ + +Routine Description: + + A wrapper function that prepare the communicate port. + +Arguments: + + SecurityDescriptor - Specifies a security descriptor to InitializeObjectAttributes(...). + + ConnectionType - The type of connection: AvConnectForScan, AvConnectForAbort, AvConnectForQuery + +Return Value: + + Returns the status of the prepartion. + +--*/ +{ + NTSTATUS status; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING uniString; + LONG maxConnections = 1; + PCWSTR portName = NULL; + PFLT_PORT *pServerPort = NULL; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV]: AvPrepareServerPort entered. \n") ); + + switch( ConnectionType ) { + case AvConnectForScan: + portName = AV_SCAN_PORT_NAME; + pServerPort = &Globals.ScanServerPort; + break; + case AvConnectForAbort: + portName = AV_ABORT_PORT_NAME; + pServerPort = &Globals.AbortServerPort; + break; + case AvConnectForQuery: + portName = AV_QUERY_PORT_NAME; + pServerPort = &Globals.QueryServerPort; + break; + default: + FLT_ASSERTMSG( "No such connection type.\n", FALSE); + break; + } + + RtlInitUnicodeString( &uniString, portName ); + + InitializeObjectAttributes( &oa, + &uniString, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + SecurityDescriptor ); + + status = FltCreateCommunicationPort( Globals.Filter, + pServerPort, // this is the output to server port. + &oa, + NULL, + AvConnectNotifyCallback, + AvDisconnectNotifyCallback, + AvMessageNotifyCallback, + maxConnections ); + + return status; +} + + diff --git a/filesys/miniFilter/avscan/filter/context.c b/filesys/miniFilter/avscan/filter/context.c new file mode 100644 index 00000000..8977014d --- /dev/null +++ b/filesys/miniFilter/avscan/filter/context.c @@ -0,0 +1,943 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + context.c + +Abstract: + + Filter Context-related module implementation. + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" + +// +// Local function prototypes. +// + +VOID +AvStreamContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +AvTransactionContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +AvSectionContextCleanup( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +AvInstanceContextCleanup( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, AvCreateStreamContext) +#pragma alloc_text(PAGE, AvCreateStreamHandleContext) +#pragma alloc_text(PAGE, AvFindOrCreateTransactionContext) +#pragma alloc_text(PAGE, AvCreateSectionContext) +#pragma alloc_text(PAGE, AvStreamContextCleanup) +#pragma alloc_text(PAGE, AvTransactionContextCleanup) +#pragma alloc_text(PAGE, AvSectionContextCleanup) +#pragma alloc_text(PAGE, AvInstanceContextCleanup) +#pragma alloc_text(PAGE, AvAllocateScanContext) +#pragma alloc_text(PAGE, AvReferenceScanContext) +#pragma alloc_text(PAGE, AvReleaseScanContext) +#endif + + +// +// Context registration structure +// + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_STREAM_CONTEXT, + 0, + AvStreamContextCleanup, + AV_STREAM_CONTEXT_SIZE, + AV_STREAM_CONTEXT_TAG }, + + { FLT_STREAMHANDLE_CONTEXT, + 0, + NULL, + AV_STREAMHANDLE_CONTEXT_SIZE, + AV_STREAMHANDLE_CONTEXT_TAG }, + + { FLT_TRANSACTION_CONTEXT, + 0, + AvTransactionContextCleanup, + AV_TRANSACTION_CONTEXT_SIZE, + AV_TRANSACTION_CONTEXT_TAG }, + + { FLT_SECTION_CONTEXT, + 0, + AvSectionContextCleanup, + AV_SECTION_CONTEXT_SIZE, + AV_SECTION_CONTEXT_TAG }, + + { FLT_INSTANCE_CONTEXT, + 0, + AvInstanceContextCleanup, + AV_INSTANCE_CONTEXT_SIZE, + AV_INSTANCE_CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + + +VOID +AvStreamContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This function is called by the filter manager before freeing any of the minifilter + driver's contexts of that type. + + In this routine, the driver has to perform any needed cleanup, such as freeing + additional memory that the minifilter driver allocated inside the context structure + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + ContextType - Supposed to be FLT_STREAM_CONTEXT. + +Return Value: + + None + +--*/ +{ + PAV_STREAM_CONTEXT streamContext = (PAV_STREAM_CONTEXT) Context; + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + FLT_ASSERTMSG( "[AV]: Stream context is not supposed to be in the transaction context list at cleanup.!\n", + NULL == streamContext->TxContext ); + + AvFreeKevent( streamContext->ScanSynchronizationEvent ); +} + +VOID +AvTransactionContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This function is called by the filter manager before freeing any of the minifilter + driver's contexts of that type. + + In this routine, the driver has to perform any needed cleanup, such as freeing + additional memory that the minifilter driver allocated inside the context structure + + We delete the stream context list in transaction context here. + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + ContextType - Supposed to be FLT_TRANSACTION_CONTEXT. + +Return Value: + + None + +--*/ +{ + PAV_TRANSACTION_CONTEXT transactionContext = (PAV_TRANSACTION_CONTEXT) Context; + + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvTransactionContextCleanup context cleanup entered.\n") ); + + ExDeleteResourceLite( transactionContext->Resource ); + AvFreeResource( transactionContext->Resource ); + transactionContext->Resource = NULL; + ObDereferenceObject( transactionContext->Transaction ); + transactionContext->Transaction = NULL; +} + +VOID +AvSectionContextCleanup( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This function is called by the filter manager before freeing any of the minifilter + driver's contexts of that type. + + In this routine, the driver has to perform any needed cleanup, such as freeing + additional memory that the minifilter driver allocated inside the context structure + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + ContextType - Supposed to be FLT_SECTION_CONTEXT (win8 or later). + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( ContextType ); + + FLT_ASSERTMSG( "[AV] AvSectionContextCleanup: Section handle should be NULL at cleanup.\n", + ((PAV_SECTION_CONTEXT) Context)->SectionHandle == NULL ); + FLT_ASSERTMSG( "[AV] AvSectionContextCleanup: Section object should be NULL at cleanup.\n", + ((PAV_SECTION_CONTEXT) Context)->SectionObject == NULL ); + +} + +VOID +AvInstanceContextCleanup( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This function is called by the filter manager before freeing any of the minifilter + driver's contexts of that type. + + In this routine, the driver has to perform any needed cleanup, such as freeing + additional memory that the minifilter driver allocated inside the context structure. + + We delete the cache table if the file system supports one. + +Arguments: + + Context - Pointer to the minifilter driver's portion of the context. + ContextType - Supposed to be FLT_INSTANCE_CONTEXT (win8 or later). + +Return Value: + + None + +--*/ +{ + + PAV_INSTANCE_CONTEXT instanceContext = (PAV_INSTANCE_CONTEXT) Context; + + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ( "[Av]: AvInstanceContextCleanup context cleanup entered\n") ); + + if (FS_SUPPORTS_FILE_STATE_CACHE( instanceContext->VolumeFSType )) { + + FLT_ASSERTMSG( "[AV] AvInstanceContextCleanup: The generic table should be empty at cleanup.\n", + RtlIsGenericTableEmpty( &instanceContext->FileStateCacheTable ) ); + ExDeleteResourceLite( &instanceContext->Resource ); + } +} + +NTSTATUS +AvCreateStreamHandleContext ( + _In_ PFLT_FILTER Filter, + _Outptr_ PAV_STREAMHANDLE_CONTEXT *StreamHandleContext + ) +/*++ + +Routine Description: + + This routine creates a new streamhandle context + +Arguments: + + StreamHandleContext - Returns the streamhandle context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PAV_STREAMHANDLE_CONTEXT streamHandleContext; + + PAGED_CODE(); + + // + // Allocate a streamhandle context + // + + status = FltAllocateContext( Filter, + FLT_STREAMHANDLE_CONTEXT, + AV_STREAMHANDLE_CONTEXT_SIZE, + PagedPool, + &streamHandleContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to allocate stream handle context with status 0x%x \n", + status) ); + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory(streamHandleContext, AV_STREAMHANDLE_CONTEXT_SIZE); + *StreamHandleContext = streamHandleContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +AvCreateStreamContext ( + _In_ PFLT_FILTER Filter, + _Outptr_ PAV_STREAM_CONTEXT *StreamContext + ) +/*++ + +Routine Description: + + This routine creates a new stream context + +Arguments: + + StreamContext - Returns the stream context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PKEVENT event = NULL; + PAV_STREAM_CONTEXT streamContext; + + PAGED_CODE(); + + // + // Allocate the kernel event object + // + + event = AvAllocateKevent(); + + if (NULL == event) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Allocate a stream context + // + + status = FltAllocateContext( Filter, + FLT_STREAM_CONTEXT, + AV_STREAM_CONTEXT_SIZE, + PagedPool, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to allocate stream context with status 0x%x \n", + status) ); + AvFreeKevent( event ); + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory(streamContext, AV_STREAM_CONTEXT_SIZE); + streamContext->ScanSynchronizationEvent = event; + KeInitializeEvent( streamContext->ScanSynchronizationEvent, SynchronizationEvent, TRUE ); + SET_FILE_MODIFIED( streamContext ); + SET_FILE_TX_MODIFIED( streamContext ); + *StreamContext = streamContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +AvFindOrCreateTransactionContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_ PAV_TRANSACTION_CONTEXT *TransactionContext + ) +/*++ + +Routine Description + + This routine finds the transaction context, if not found, it will + try to create a new one. The caller is responsible for calling + FltReleaseContext to decrement its reference count. + +Arguments + + FltObjects - Contains parameters required to enlist in a transaction. + + TransactionContext - Returns the transaction context + +Return value + + Returns STATUS_SUCCESS if we were able to successfully find/create + a transaction context. Returns an appropriate error code on a failure. + +--*/ +{ + NTSTATUS status; + PAV_TRANSACTION_CONTEXT transactionContext = NULL; + PAV_TRANSACTION_CONTEXT oldTransactionContext = NULL; + PERESOURCE pResource = NULL; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvFindOrCreateTransactionContext entered. \n") ); + + status = FltGetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + if (NT_SUCCESS( status )) { + + *TransactionContext = transactionContext; + return STATUS_SUCCESS; + } + + if (status != STATUS_NOT_FOUND) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: Failed to get transaction context with status 0x%x \n", + status) ); + return status; + } + + // + // Allocate the resource + // + + pResource = AvAllocateResource(); + + if ( NULL == pResource ) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Allocate a transaction context. + // + + status = FltAllocateContext( Globals.Filter, + FLT_TRANSACTION_CONTEXT, + AV_TRANSACTION_CONTEXT_SIZE, + PagedPool, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV]: Failed to allocate transaction context with status 0x%x \n", + status) ); + AvFreeResource( pResource ); + return status; + } + + FLT_ASSERTMSG( "[AV]: Transaction object pointer is not supposed to be NULL !\n", FltObjects->Transaction != NULL); + + // + // Initialization of transaction context. + // The reason we allocate eResource seperately is because + // eResource has to be allocated in the non-paged pool. + // + + RtlZeroMemory(transactionContext, AV_TRANSACTION_CONTEXT_SIZE); + transactionContext->Resource = pResource; + ObReferenceObject( FltObjects->Transaction ); + transactionContext->Transaction = FltObjects->Transaction; + InitializeListHead( &transactionContext->ScListHead ); + ExInitializeResourceLite( transactionContext->Resource ); + + status = FltSetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + transactionContext, + &oldTransactionContext ); + + if (NT_SUCCESS( status )) { + + *TransactionContext = transactionContext; + return STATUS_SUCCESS; + } + + FltReleaseContext( transactionContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to set transaction context with status 0x%x \n", + status) ); + + return status; + } + + if (NULL == oldTransactionContext) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to set transaction context oldTransactionContext is NULL \n") ); + + return status; + } + + *TransactionContext = oldTransactionContext; + + + return STATUS_SUCCESS; +} + +NTSTATUS +AvCreateSectionContext ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Outptr_ PAV_SECTION_CONTEXT *SectionContext + ) +/*++ + +Routine Description: + + This routine creates a new section context. + +Arguments: + + Instance - Opaque instance pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + SectionContext - Returns the section context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + LONGLONG fileSize; + PAV_SECTION_CONTEXT sectionContext = NULL; + + PAGED_CODE(); + + status = FltAllocateContext( Globals.Filter, + FLT_SECTION_CONTEXT, + AV_SECTION_CONTEXT_SIZE, + PagedPool, + §ionContext ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to allocate section context.\n, 0x%08x\n", + status) ); + return status; + } + + RtlZeroMemory(sectionContext, AV_SECTION_CONTEXT_SIZE); + + status = AvGetFileSize( Instance, + FileObject, + &fileSize ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: Failed to get file size with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + FileObject, + Instance )); + } else { + + sectionContext->FileSize = fileSize; + } + + *SectionContext = sectionContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +AvEnumerateInstances( + _Outptr_result_buffer_(*NumberInstances) PFLT_INSTANCE **InstanceArray, + _Out_ PULONG NumberInstances + ) +/*++ + +Routine Description: + + This routine returns all the instances available of this filter + The caller is responsible for calling AvFreeInstances(...) to release the instance references. + +Arguments: + + InstanceArray - This function will allocate the memory of the arrary containing the instances. + + NumberInstances - The number of instances in InstanceArray. + +Return Value: + + Status + +--*/ +{ + PFLT_INSTANCE *instArray = NULL; + NTSTATUS status = STATUS_SUCCESS; + ULONG i = 0; + ULONG instCnt = 0; + ULONG newCount = 0; + + + // + // Get a count of how many instances there are + // + + status = FltEnumerateInstances( NULL, + Globals.Filter, + NULL, + 0, + &instCnt ); + + if (!NT_SUCCESS(status) && + (status != STATUS_BUFFER_TOO_SMALL)) { + + goto Cleanup; + } + + // + // Get handles for all instances. This will loop in case too many + // filters load between the time we got the count and the time + // we actually get the list. + // + + for (;;) { + + // + // Free old memory if we have some + // + + if (instArray != NULL) { + + ExFreePoolWithTag( instArray, AV_INSTANCES_ARRAY_TAG); + instArray = NULL; + } + + // + // Allocate memory for list, add a couple of entries in case + // a filter loads while we are doing this + // + + instCnt += 2; + + instArray = ExAllocatePoolWithTag( PagedPool, + (instCnt * sizeof(PFLT_INSTANCE)), + AV_INSTANCES_ARRAY_TAG ); + + if (instArray == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + // + // This time get list of filters (and a new count) + // + + status = FltEnumerateInstances( NULL, + Globals.Filter, + instArray, + instCnt, + &newCount ); + + // + // exit loop if we succeeded + // + + if (NT_SUCCESS(status)) { + + instCnt = newCount; + break; + } + + // + // If it was an unexpected error, quit processing, else allocate + // more memory and try again + // + + if (status != STATUS_BUFFER_TOO_SMALL) { + + goto Cleanup; + } + + // + // The buffer was too small, try again + // + + FLT_ASSERT(newCount > instCnt); + instCnt = newCount; + } + + *InstanceArray = instArray; + *NumberInstances = instCnt; + +Cleanup: + + if ( !NT_SUCCESS(status) ) { + + if (instArray) { + + // + // Release all the objects in the array + // + + for (i = 0; i < instCnt; i++) { + + FltObjectDereference( instArray[i] ); + instArray[i] = NULL; + } + + ExFreePoolWithTag( instArray, AV_INSTANCES_ARRAY_TAG ); + instArray = NULL; + } + } + + + return status; + +} + +VOID +AvFreeInstances ( + _In_reads_(InstanceCount) PFLT_INSTANCE *InstanceArray, + _In_ ULONG InstanceCount + ) +/*++ + +Routine Description: + + This routine frees the reference count and memory of instance array obtained from AvEnumerateInstances(...). + +Arguments: + + InstanceArray - The instance arrary to be freed. + + NumberInstances - The number of instances in InstanceArray. + +Return Value: + + None. + +--*/ +{ + ULONG i = 0; + + // + // Release all the objects in the array + // + + for (i = 0; i < InstanceCount; i++) { + + FltObjectDereference( InstanceArray[i] ); + InstanceArray[i] = NULL; + } + + ExFreePoolWithTag( InstanceArray, AV_INSTANCES_ARRAY_TAG ); +} + +NTSTATUS +AvAllocateScanContext( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Outptr_ PAV_SCAN_CONTEXT *ScanContext + ) +/*++ + +Routine Description: + + The routine allocates the scan context + +Arguments: + + Instance - Opaque instance pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + ScanContext - The output scan context. + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES if allocation failed. + STATUS_SUCCESS if successfully allocated. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PAV_SCAN_CONTEXT scanCtx = NULL; + + PAGED_CODE(); + + ASSERT(Instance != NULL); + ASSERT(FileObject != NULL); + + scanCtx = ExAllocatePoolWithTag( NonPagedPoolNx, + sizeof(AV_SCAN_CONTEXT), + AV_SCAN_CTX_TAG ); + + if (NULL == scanCtx) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + scanCtx->RefCount = 1; + scanCtx->IoWaitOnScanCompleteNotificationAborted = FALSE; + + // + // Keeps a reference in scan contex. + // We also handle the case that the instance is being torn down. + // + status = FltObjectReference( Instance ); + if (!NT_SUCCESS(status)) { + + ExFreePoolWithTag( scanCtx, AV_SCAN_CTX_TAG ); + return status; + } + scanCtx->FilterInstance = Instance; + + // + // Keeps a reference in scan context + // + ObReferenceObject( FileObject ); + scanCtx->FileObject = FileObject; + + *ScanContext = scanCtx; + return STATUS_SUCCESS; +} + +NTSTATUS +AvReferenceScanContext( + _In_ PAV_SCAN_CONTEXT ScanContext + ) +/*++ + +Routine Description: + + The routine increments the reference count of scan context to prevent it from deletion. + +Arguments: + + ScanContext - The scan context to be added reference. + +Return Value: + + STATUS_INVALID_PARAMETER if ScanContext is NULL. + STATUS_SUCCESS if successfully incremented. + +--*/ +{ + PAGED_CODE(); + + if (ScanContext == NULL) { + + return STATUS_INVALID_PARAMETER; + } + + ASSERT(ScanContext->RefCount != 0); + ASSERT(ScanContext->FilterInstance != NULL); + ASSERT(ScanContext->FileObject != NULL); + + InterlockedIncrement(&ScanContext->RefCount); + + return STATUS_SUCCESS; +} + +NTSTATUS +AvReleaseScanContext( + _In_ PAV_SCAN_CONTEXT ScanContext + ) +/*++ + +Routine Description: + + The routine decrements the reference count of scan context. + Release it if reference count goes to zero. + +Arguments: + + ScanContext - The scan context to be released. + +Return Value: + + STATUS_INVALID_PARAMETER if ScanContext is NULL. + STATUS_SUCCESS if successfully decremented. + +--*/ +{ + ULONG newRefCount = 0; + + PAGED_CODE(); + + if (ScanContext == NULL) { + + return STATUS_INVALID_PARAMETER; + } + + ASSERT(ScanContext->FilterInstance != NULL); + ASSERT(ScanContext->FileObject != NULL); + + // + // Assume the usage of AvReferenceScanContext and AvReleaseScanContext are not raced, + // This simple version would suffice. + // + + newRefCount = InterlockedDecrement(&ScanContext->RefCount); + if (newRefCount == 0) { + + // + // Before freeing scan context, we need to release the file object and instance. + // + FltObjectDereference( ScanContext->FilterInstance ); + ObDereferenceObject( ScanContext->FileObject ); + ExFreePoolWithTag( ScanContext, AV_SCAN_CTX_TAG ); + } + return STATUS_SUCCESS; +} + + diff --git a/filesys/miniFilter/avscan/filter/context.h b/filesys/miniFilter/avscan/filter/context.h new file mode 100644 index 00000000..f9c0eeec --- /dev/null +++ b/filesys/miniFilter/avscan/filter/context.h @@ -0,0 +1,357 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + context.h + +Abstract: + + Header file which contains context-related data + structures, type definitions, constants, + global variables and function prototypes. + +Environment: + + Kernel mode + +--*/ + +#ifndef __CONTEXT_H__ +#define __CONTEXT_H__ + +// +// The file infected state. +// + +typedef enum _AV_FILE_INFECTED_STATE { + + AvFileUnknown, + AvFileInfected, + AvFileNotInfected, // clean. + AvFileModified, + AvFileScanning + +} AV_FILE_INFECTED_STATE; + +#define AV_STREAMHANDLE_CONTEXT_TAG 'hSvA' +#define AV_STREAM_CONTEXT_TAG 'cSvA' +#define AV_TRANSACTION_CONTEXT_TAG 'cTvA' +#define AV_SECTION_CONTEXT_TAG 'eSvA' +#define AV_INSTANCE_CONTEXT_TAG 'cIvA' +#define AV_INSTANCES_ARRAY_TAG 'aIvA' +#define AV_CONNECTION_CTX_TAG 'cCvA' +#define AV_SCAN_CTX_TAG 'cMvA' + +// +// Defines the transaction context structure +// +#define AV_TXCTX_ENLISTED 0x01 +#define AV_TXCTX_LISTDRAINED 0x02 + +typedef struct _AV_TRANSACTION_CONTEXT { + + // + // Transaction object pointer + // + + PKTRANSACTION Transaction; + + // + // List head for stream context list. + // + + LIST_ENTRY ScListHead; + + // + // Lock used to protect this context. + // + + PERESOURCE Resource; + + // + // A flag that tracks: + // AV_TXCTX_ENLISTED: if it has been enlisted in transaction + // AV_TXCTX_LISTDRAINED: list is drained. + // + + ULONG Flags; + +} AV_TRANSACTION_CONTEXT, *PAV_TRANSACTION_CONTEXT; + +#define AV_TRANSACTION_CONTEXT_SIZE sizeof( AV_TRANSACTION_CONTEXT ) + + +#define IS_FILE_MODIFIED( _sCtx ) ( (_sCtx)->State == AvFileModified ) +#define IS_FILE_INFECTED( _sCtx ) ( (_sCtx)->State == AvFileInfected ) +#define IS_FILE_NOT_INFECTED( _sCtx ) ( (_sCtx)->State == AvFileNotInfected ) + +#define IS_FILE_TX_MODIFIED( _sCtx ) ( (_sCtx)->TxState == AvFileModified ) +#define IS_FILE_TX_INFECTED( _sCtx ) ( (_sCtx)->TxState == AvFileInfected ) +#define IS_FILE_TX_NOT_INFECTED( _sCtx ) ( (_sCtx)->TxState == AvFileNotInfected ) + +#define IS_FILE_NEED_SCAN( _sCtx ) ((((_sCtx)->TxContext == NULL) && IS_FILE_MODIFIED( _sCtx )) || \ + (((_sCtx)->TxContext != NULL) && IS_FILE_TX_MODIFIED( _sCtx ))) + + +#define SET_FILE_UNKNOWN( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileUnknown) +#define SET_FILE_MODIFIED( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileModified) +#define SET_FILE_INFECTED( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileInfected) +#define SET_FILE_NOT_INFECTED( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileNotInfected) +#define SET_FILE_SCANNING( _sCtx ) InterlockedExchange(&(_sCtx)->State, AvFileScanning) + +#define SET_FILE_TX_UNKNOWN( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileUnknown) +#define SET_FILE_TX_MODIFIED( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileModified) +#define SET_FILE_TX_INFECTED( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileInfected) +#define SET_FILE_TX_NOT_INFECTED( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileNotInfected) +#define SET_FILE_TX_SCANNING( _sCtx ) InterlockedExchange(&(_sCtx)->TxState, AvFileScanning) + +#define SET_FILE_UNKNOWN_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_UNKNOWN( _sCtx ); \ + } else { \ + SET_FILE_UNKNOWN( _sCtx ); \ + } \ + } +#define SET_FILE_MODIFIED_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_MODIFIED( _sCtx ); \ + } else { \ + SET_FILE_MODIFIED( _sCtx ); \ + } \ + } +#define SET_FILE_INFECTED_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_INFECTED( _sCtx ); \ + } else { \ + SET_FILE_INFECTED( _sCtx ); \ + } \ + } +#define SET_FILE_NOT_INFECTED_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_NOT_INFECTED( _sCtx ); \ + } else { \ + SET_FILE_NOT_INFECTED( _sCtx ); \ + } \ + } +#define SET_FILE_SCANNING_EX( _flag, _sCtx ) {\ + if (_flag) { \ + SET_FILE_TX_SCANNING( _sCtx ); \ + } else { \ + SET_FILE_SCANNING( _sCtx ); \ + } \ + } + +// +// Stream/Stream Handle flags +// + +#define AV_FLAG_PREFETCH 0x00000001 + +typedef struct _AV_STREAMHANDLE_CONTEXT { + + // + // Handle flags + // + + ULONG Flags; + +} AV_STREAMHANDLE_CONTEXT, *PAV_STREAMHANDLE_CONTEXT; + +#define AV_STREAMHANDLE_CONTEXT_SIZE sizeof( AV_STREAMHANDLE_CONTEXT ) + +typedef struct _AV_STREAM_CONTEXT { + + // + // Stream flags + // + + ULONG Flags; + + // + // File ID, obtained from querying the file system for + // FileInternalInformation or FileIdInformation. + // + + AV_FILE_REFERENCE FileId; + + // + // A pointer to the transaction context, so we can jump to list in the transaction. + // + + PAV_TRANSACTION_CONTEXT TxContext; + + // + // This list entry is exactly the embedded entry to + // form a doubly linked list inside transaction context. + // + + LIST_ENTRY ListInTransaction; + + // + // We need to synchronize the creation of the section object. + // If this syncrhonization is not made, FltCreateSectionForDataScan + // would return STATUS_FLT_CONTEXT_ALREADY_DEFINED when two threads + // are about to create the section for the same file. + // + + PKEVENT ScanSynchronizationEvent; + + // + // Please see AV_FILE_INFECTED_STATE for the definition of file state + // Note that we have TxState to maintain the isolation of + // the transacted writer's view. + // + + volatile LONG State; + + + volatile LONG TxState; + + // + // Revision numbers for files on CSVFS + // + LONGLONG VolumeRevision; + LONGLONG CacheRevision; + LONGLONG FileRevision; + +} AV_STREAM_CONTEXT, *PAV_STREAM_CONTEXT; + +#define AV_STREAM_CONTEXT_SIZE sizeof( AV_STREAM_CONTEXT ) + +// +// Defines the section context structure +// + +typedef struct _AV_SECTION_CONTEXT { + + // + // The associated section handle. + // + + HANDLE SectionHandle; + + // + // The associated section object. + // + + PVOID SectionObject; + + // + // The cancel flag (if scan in the kernel mode). + // + + BOOLEAN Aborted; + + + // + // The size of the file associated with the section object. + // + + LONGLONG FileSize; + + // + // This flag indicates if this section data scan can be cancelable. + // Right now, only at pre-cleanup is cancelable on conflicting Io. + // + + BOOLEAN CancelableOnConflictingIo; + + // + // In the context of a conflict notification callback, only section context is given. + // We need to remember associated scan context to have scan id, so that + // We know which scan to cancel. + // + PVOID ScanContext; + +} AV_SECTION_CONTEXT, *PAV_SECTION_CONTEXT; + +#define AV_SECTION_CONTEXT_SIZE sizeof( AV_SECTION_CONTEXT ) + +// +// Instance context +// + +typedef struct _AV_INSTANCE_CONTEXT { + + // + // The associated volume object pointer + // + + PFLT_VOLUME Volume; + + // + // The associated filter instance pointer + // + + PFLT_INSTANCE Instance; + + // + // The file system type of the volume + // + + FLT_FILESYSTEM_TYPE VolumeFSType; + + // + // If the file system is NTFS, then it will support a file state cache table + // that saves the state of the file. + // + + RTL_GENERIC_TABLE FileStateCacheTable; + + // + // The per-instance lock to protect the cache table above. + // + + ERESOURCE Resource; + + // + // When set this flag indicates that the filter is attached on the + // hidden NTFS volume corresponding to a CSVFS volume + // + BOOLEAN IsOnCsvMDS; + +} AV_INSTANCE_CONTEXT, *PAV_INSTANCE_CONTEXT; + +#define AV_INSTANCE_CONTEXT_SIZE sizeof( AV_INSTANCE_CONTEXT ) + +NTSTATUS +AvFindOrCreateTransactionContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_ PAV_TRANSACTION_CONTEXT *TransactionContext + ); + +NTSTATUS +AvCreateSectionContext ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Outptr_ PAV_SECTION_CONTEXT *SectionContext + ); + +NTSTATUS +AvCreateStreamHandleContext ( + _In_ PFLT_FILTER Filter, + _Outptr_ PAV_STREAMHANDLE_CONTEXT *StreamHandleContext + ); + +NTSTATUS +AvCreateStreamContext ( + _In_ PFLT_FILTER Filter, + _Outptr_ PAV_STREAM_CONTEXT *StreamContext + ); + +NTSTATUS +AvEnumerateInstances( + _Outptr_result_buffer_(*NumberInstances) PFLT_INSTANCE **InstanceArray, + _Out_ PULONG NumberInstances + ); + +VOID +AvFreeInstances ( + _In_reads_(InstanceCount) PFLT_INSTANCE *InstanceArray, + _In_ ULONG InstanceCount + ); + +#endif + diff --git a/filesys/miniFilter/avscan/filter/csvfs.c b/filesys/miniFilter/avscan/filter/csvfs.c new file mode 100644 index 00000000..8d29a7fc --- /dev/null +++ b/filesys/miniFilter/avscan/filter/csvfs.c @@ -0,0 +1,1039 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + csvfs.c + +Abstract: + + This is the csvfs specific module of the avscan mini-filter driver. + This filter demonstrates how to implement a transaction-aware + anti-virus filter. + + Av prefix denotes "Anti-virus" module. + + + CSVFS is a distributed file system where multiple nodes in a + cluster can expose the same volume namespace at the same time. To + achieve this the CSVFS volumes on each node act as a proxy for an + underlying NTFS volume where the NTFS volume runs only on one node + of the cluster. It is this NTFS volume where the file data and file + system metadata are stored. The node that exposes this NTFS volume + is the coordinator node. The underlying NTFS volume is made hidden + to keep applications from using it directly. All applications + should use the CSVFS namespace. + + CSVFS implements functionality known as Direct I/O. This + functionality allow the CSVFS proxy on each node to directly read + or write to the blocks on the disk and bypass the NTFS volume on the + coordinator node when it is safe to do so. A filter on node node + needs to be aware that it may not see all I/O to a file. + + In the CSVFS enviornment filters can layer on the CSVFS volume + stack, the hidden NTFS volume stack on the coordinator node and + also on the MUP stack. When on the MUP and the hidden NTFS stack + the filter should not scan any files that are opened with + GUID_ECP_CSV_DOWN_LEVEL_OPEN ECP attached. This ECP is used by + CSVFS for its internal file opens and should be ignored by filters. + Filters that layer on the CSVFS would be the components that scan + the files. + + It is recommended that filters be extremely careful when layered on + the hidden NTFS stack: + + Management issues + Volume is hidden and thus does not have volume guid, + mountpoint or drive letter that can be used to + represent this volume to the user in a command line or UI. + + The coordinator node for a CSVFS volume can move to another + node at any time. This would cause challenges with + maintaining filter configuration setttings and also be + challenging for the admin to know which node should be used. + + Interop with CSVFS issues + Changing file sizes in a filter layered above the hidden + NTFS volume may cause data corruption. + + Building and keeping a mapped section on hidden NTFS volume + might prevent direct IO from happening + + Building and keeping mapped section on hidden NTFS volume + might lead to cache coherency issues (stale cache) and + eventually to data corruption. + + Note that there is a feasible model for creating a distributed + filter with instances on the CSVFS and the hidden NTFS volume + stacks. The instance on the hidden NTFS volume would serve as a + centralized filter meta-data server where information shared + between node could be maintained. Each instance on CSVFS volume + would communicate with the hidden NTFS volume stack through a + downlevel file handle. + + CSVFS provides a very simple centralized meta-data cache which + contains revision numbers for tracking changes to files. This + sample shows how those revision numbers can be used to determine if + a file has been modified by another node and thus if it needs to be + scanned locally. + + Other things to be aware of: + + Filter should not have *any* global locks or else deadlock may + occur. File system requests will flow through the CSVFS proxy file + system and then be forwarded to the hidden NTFS file system. If the + filter is layered on both file systems and the filter instance on the + CSVFS volume takes a lock as the request passes through, when the + request is forwarded and reaches the filter instance on the hidden + NTFS or MUP volume, it will attempt to acquire the lock and deadlock will + occur. + + Do not make assumption that buffered IO will always go to cache and + eventually you will see paging IO. CsvFs, like RDR, does caching based + on the oplock it was able to get for this stream. For instance if a + file is being accessed from multiple nodes CsvFs would have oplock + level RH or R or none and all cached writes to the file will be sent + directly to NTFS without going to CC. You can think about that as if + from the perspective of a filter sitting above CSVFS cached IO is being + handles as if is it un buffered IO. Since in general filters would + not know when CsvFs loses/gains oplocks they should not make an + assumption that cached IO will go through CC. + + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" +#include <ntdddisk.h> + +/************************************************************************* + Local Function Prototypes +*************************************************************************/ +NTSTATUS +AvAddCsvRevisionECP ( + _Inout_ PFLT_CALLBACK_DATA Data + ); + +NTSTATUS +AvReadCsvRevisionECP ( + _Inout_ PFLT_CALLBACK_DATA Data, + _Out_ LONGLONG *VolumeRevision, + _Out_ LONGLONG *CacheRevision, + _Out_ LONGLONG *FileRevision + ); + +NTSTATUS +AvQueryCsvRevisionNumbers ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Out_ LONGLONG *VolumeRevision, + _Out_ LONGLONG *CacheRevision, + _Out_ LONGLONG *FileRevision + ); + +NTSTATUS +AvFindAckedECP ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ LPCGUID EcpGuid, + _Out_ PVOID *Ecp, + _Out_ ULONG *EcpSize + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, AvIsVolumeOnCsvDisk) +#pragma alloc_text(PAGE, AvIsCsvDlEcpPresent) +#pragma alloc_text(PAGE, AvPreCreateCsvfs) +#pragma alloc_text(PAGE, AvPostCreateCsvfs) +#pragma alloc_text(PAGE, AvPreCleanupCsvfs) +#pragma alloc_text(PAGE, AvQueryCsvRevisionNumbers) +#pragma alloc_text(PAGE, AvReadCsvRevisionECP) +#pragma alloc_text(PAGE, AvAddCsvRevisionECP) +#pragma alloc_text(PAGE, AvFindAckedECP) +#endif + +BOOLEAN +AvIsVolumeOnCsvDisk ( + _In_ PFLT_VOLUME Volume + ) + /*++ + + Routine Description: + + This routine checks if the volume indicated by Volume belongs to a disk that is CSV or not. + + Arguments: + + Volume - Pointer to the FLT_VOLUME. + + Return Value: + + The return value is TRUE or FALSE. + + --*/ +{ + + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN retValue = FALSE; + PDEVICE_OBJECT disk = NULL, refDeviceObject=NULL; + PIRP irp; + IO_STATUS_BLOCK iosb; + ULONG controlCode = IOCTL_DISK_GET_CLUSTER_INFO; + DISK_CLUSTER_INFO outBuf; + KEVENT event; + + PAGED_CODE(); + + status = FltGetDiskDeviceObject(Volume, &disk); + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("Failed to get disk object from volume, status 0x%x\n", status) ); + goto Cleanup; + } + + refDeviceObject = IoGetAttachedDeviceReference(disk); + + iosb.Information = 0; + RtlZeroMemory(&outBuf, sizeof(outBuf)); + KeInitializeEvent(&event, NotificationEvent, FALSE); + + irp = IoBuildDeviceIoControlRequest( controlCode, + refDeviceObject, + NULL, + 0, + &outBuf, + sizeof(outBuf), + FALSE, + &event, + &iosb ); + if (irp == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("Failed to allocate Irp, status 0x%x\n", status) ); + goto Cleanup; + } + + status = IoCallDriver( refDeviceObject, irp ); + if (status == STATUS_PENDING) { + KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); + status = iosb.Status; + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("DeviceIoControl returned status 0x%x\n", status) ); + + if(!NT_SUCCESS( status )) { + goto Cleanup; + } + + retValue = FlagOn( outBuf.Flags, DISK_CLUSTER_FLAG_CSV ) ? TRUE : FALSE; + if (FlagOn( outBuf.Flags, DISK_CLUSTER_FLAG_CSV) && FlagOn(outBuf.Flags, DISK_CLUSTER_FLAG_IN_MAINTENANCE )) { + // + // A CSV disk can be in maintenance mode. When in maintenance + // mode the CSV namespace is no longer exposed across the + // entire cluster but instead only exposed on the single node + // where the NTFS volume is exposed. In this case the filter + // should treat the volume as it would any other NTFS volume + // + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("Disk is CSV but in Maintenance\n") ); + retValue = FALSE; + } + + if(retValue == TRUE) { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("Disk is CSV\n") ); + } + else { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("Disk is not CSV\n") ); + } + +Cleanup: + + if (refDeviceObject) { + KeEnterCriticalRegion(); + ObDereferenceObject( refDeviceObject ); + refDeviceObject = NULL; + KeLeaveCriticalRegion(); + } + + if (disk) { + ObDereferenceObject( disk ); + disk = NULL; + } + + return retValue; +} + +NTSTATUS +AvAddCsvRevisionECP ( + _Inout_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This routine will include the Extra Create Parameter (ECP) that is + used on CSVFS file systems. This ECP will return the set of + revision numbers that are associated with the file being opened. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList = NULL; + PCSV_QUERY_FILE_REVISION_ECP_CONTEXT ecpContext; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvAddCsvRevisionECP: Entered\n") ); + + status = FltGetEcpListFromCallbackData( Globals.Filter, + Data, + &ecpList ); + if (!NT_SUCCESS( status )) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltGetEcpListFromCallbackData failed 0x%x\n", status) ); + goto Cleanup; + } + + if (ecpList == NULL) { + // + // Create a new ecplist. + // + status = FltAllocateExtraCreateParameterList( Globals.Filter, 0, &ecpList ); + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltAllocateExtraCreateParameterList failed 0x%x", status) ); + goto Cleanup; + } + // + // Set it into CBD. + // + status = FltSetEcpListIntoCallbackData( Globals.Filter, Data, ecpList ); + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltSetEcpListIntoCallbackData failed 0x%x", status) ); + FltFreeExtraCreateParameterList( Globals.Filter, ecpList ); + goto Cleanup; + } + + } else { + // + // See if the ECP has already been added to the ECP list + // already. + // + status = FltFindExtraCreateParameter( Globals.Filter, + ecpList, + &GUID_ECP_CSV_QUERY_FILE_REVISION, + NULL, + NULL ); + if (status != STATUS_NOT_FOUND) { + goto Cleanup; + } + + } + + status = FltAllocateExtraCreateParameter( Globals.Filter, + &GUID_ECP_CSV_QUERY_FILE_REVISION, + sizeof(CSV_QUERY_FILE_REVISION_ECP_CONTEXT), + 0, + NULL, + AV_SCAN_CTX_TAG, + &ecpContext ); + + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltAllocateExtraCreateParameterFromLookasideList failed 0x%x\n", status) ); + goto Cleanup; + } + + RtlZeroMemory( ecpContext, sizeof(CSV_QUERY_FILE_REVISION_ECP_CONTEXT )); + status = FltInsertExtraCreateParameter( Globals.Filter, + ecpList, + ecpContext ); + if (!NT_SUCCESS(status)) { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvAddCsvRevisionECP: FltInsertExtraCreateParameter failed 0x%x\n", status) ); + FltFreeExtraCreateParameter( Globals.Filter, ecpContext ); + goto Cleanup; + } + +Cleanup: + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvAddCsvRevisionECP: Leave\n") ); + + return status; +} + +NTSTATUS +AvFindAckedECP ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ LPCGUID EcpGuid, + _Out_ PVOID *Ecp, + _Out_ ULONG *EcpSize + ) +/*++ + +Routine Description: + + This routine will find the Extra Create Parameter (ECP) and if it + exists then check if it has been acknowledged + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + EcpGuid - Pointer to the guid that represents the ECP to find + + *Ecp - returns with a pointer to the ECP data + + *EcpSize - returns with the size of the ECP data + + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList = NULL; + PVOID ecpContext = NULL; + ULONG ecpContextSize = 0; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvFindAckedECP: Entered\n") ); + + status = FltGetEcpListFromCallbackData( Globals.Filter, + Data, + &ecpList); + if (NT_SUCCESS(status)) { + + if (ecpList != NULL) { + + status = FltFindExtraCreateParameter( Globals.Filter, + ecpList, + EcpGuid, + &ecpContext, + &ecpContextSize); + + if (NT_SUCCESS(status)) { + + if (FltIsEcpAcknowledged( Globals.Filter, ecpContext )) { + *Ecp = ecpContext; + *EcpSize = ecpContextSize; + } else { + status = STATUS_UNSUCCESSFUL; + } + } + + } else { + status = STATUS_UNSUCCESSFUL; + } + } + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvFindAckedECP: leave 0x%x\n", status) ); + + return status; +} + +NTSTATUS +AvReadCsvRevisionECP ( + _Inout_ PFLT_CALLBACK_DATA Data, + _Out_ LONGLONG *VolumeRevision, + _Out_ LONGLONG *CacheRevision, + _Out_ LONGLONG *FileRevision + ) +/*++ + +Routine Description: + + This routine will read the Extra Create Parameter (ECP) that is + returned on CSVFS file systems. This ECP returns the set of + revision numbers that are associated with the file being opened. + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + + *VolumeRevision returns with the volume revision number + + *CacheRevision returns with the cache revision number + + *FileRevision returns with the file revision number + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PCSV_QUERY_FILE_REVISION_ECP_CONTEXT ecpContext = NULL; + ULONG ecpContextSize = 0; + + PAGED_CODE(); + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvReadCsvRevisionECP: Entered\n") ); + + + status = AvFindAckedECP( Data, + &GUID_ECP_CSV_QUERY_FILE_REVISION, + &ecpContext, + &ecpContextSize ); + + if (NT_SUCCESS( status )) { + *VolumeRevision = ecpContext->FileRevision[0]; + *CacheRevision = ecpContext->FileRevision[1]; + *FileRevision = ecpContext->FileRevision[2]; + } + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvReadCsvRevisionECP: leave 0x%x\n", status) ); + + return status; +} + +NTSTATUS +AvQueryCsvRevisionNumbers ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Out_ LONGLONG *VolumeRevision, + _Out_ LONGLONG *CacheRevision, + _Out_ LONGLONG *FileRevision + ) +/*++ + +Routine Description: + + Obtain the most updated revision numbers for a file on CSVFS + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + *VolumeRevision returns with the volume revision number + + *CacheRevision returns with the cache revision number + + *FileRevision returns with the file revision number + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + CSV_CONTROL_PARAM request; + CSV_QUERY_FILE_REVISION revision; + ULONG bytesReturned; + + PAGED_CODE(); + + RtlZeroMemory( &request, sizeof( request ) ); + request.Operation = CsvControlQueryFileRevision; + + status = FltFsControlFile( FltObjects->Instance, + FltObjects->FileObject, + FSCTL_CSV_CONTROL, + &request, + sizeof(request), + &revision, + sizeof(revision), + &bytesReturned ); + + if (NT_SUCCESS( status )) { + *VolumeRevision = revision.FileRevision[0]; + *CacheRevision = revision.FileRevision[1]; + *FileRevision = revision.FileRevision[2]; + } + + return status; +} + +BOOLEAN +AvIsCsvDlEcpPresent ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This local function will determine if there is a CSVFS downlevel + ECP attached. + +Arguments: + + Filter - Pointer to the filter structure + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - CSVFS downlevel ECP is present + FALSE - CSVFS downlevel ECP is not present or an error occured + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList; + PVOID ecpContext; + + PAGED_CODE(); + + status = FltGetEcpListFromCallbackData( Filter, Data, &ecpList ); + + if (NT_SUCCESS(status) && (ecpList != NULL)) { + + status = FltFindExtraCreateParameter( Filter, + ecpList, + &GUID_ECP_CSV_DOWN_LEVEL_OPEN, + &ecpContext, + NULL ); + + if (NT_SUCCESS(status)) { + + return TRUE; + } + } + + return FALSE; +} + +NTSTATUS +AvPreCreateCsvfs ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects + ) +/*++ + +Routine Description: + + This function implements the PreCreate processing associated with a + CSVFS volume. The work done is to include the file revision ECP + into the ECP list so that CSVFS will return the revision numbers + when the create completes. + +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. + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PAV_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) { + // + // Add ECP to retrieve the revision numbers + // + status = AvAddCsvRevisionECP( Data ); + + // + // we don't worry if this fails since if we do not get the + // revision numbers then we just assume they have changed + // + } + + FltReleaseContext( instanceContext ); + } else { + // + // If unable to get instance context then it is no problem. It + // means that the revision numbers won't be returned from + // CSVFS. It is not fatal but will result in the file being + // rescanned. + // + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPreCreateCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) ); + } + + return status; +} + +NTSTATUS +AvPostCreateCsvfs ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _Out_ BOOLEAN *UpdateRevisionNumbers, + _Out_ LONGLONG *VolumeRevisionPtr, + _Out_ LONGLONG *CacheRevisionPtr, + _Out_ LONGLONG *FileRevisionPtr + ) +/*++ + +Routine Description: + + This function implements the PostCreate processing associated with a + CSVFS volume. The work done is to determine if the revision numbers + have been returned and if so determine if a rescan of the file is + needed. A recan would be needed if the revision numbers are not + able to be retrieved, are not valid or do not match the previously + recorded revision numbers. + +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. + + StreamContext - Pointer to the AV stream context + + *UpdateRevisionNumbers returns TRUE if the revision numbers should + be updated in the stream context upon a successful scan + + *VolumeRevisionPtr return with the updated volume revision number + + *CacheRevisionPtr return with the updated cache revision number + + *FileRevisionPtr return with the updated file revision number + +Return Value: + + Status + +--*/ +{ + PAV_INSTANCE_CONTEXT instanceContext = NULL; + NTSTATUS status; + LONGLONG VolumeRevision = 0, CacheRevision = 0, FileRevision = 0; + BOOLEAN needRescanOnCsvfs = FALSE; + + PAGED_CODE(); + + *UpdateRevisionNumbers = FALSE; + *VolumeRevisionPtr = 0; + *CacheRevisionPtr = 0; + *FileRevisionPtr = 0; + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) { + + // + // Read ECP to retrieve the revision numbers + // + status = AvReadCsvRevisionECP( Data, + &VolumeRevision, + &CacheRevision, + &FileRevision); + + if (NT_SUCCESS( status )) { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + (" [AV] AvPostCreateCsvfs: %I64x:%I64x:%I64x\n", + VolumeRevision, + CacheRevision, + FileRevision) ); + + // + // It is very possible that the file was changed by + // another node in the cluster and so we need to check + // this case. So if any of the revision number have + // changed we need to assume that the file has changed. + // Note that this is a very pessimistic assumption as + // the Volume and Cache revision numbers could change + // without a corresponding file change but rescanning + // when these change will ensure that a file changed + // on another node will not be opened without being + // rescanned on this node. + // + // Also note that there are cases where the revision + // numbers are zero and thus not at all valid. In this + // case the file must be rescanned as it is not known + // if it was changed or not. + // + needRescanOnCsvfs = ( (VolumeRevision == 0) || + (CacheRevision == 0) || + (FileRevision == 0) || + (VolumeRevision != StreamContext->VolumeRevision) || + (CacheRevision != StreamContext->CacheRevision) || + (FileRevision != StreamContext->FileRevision) ); + } else { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + (" [AV] AvPostCreateCsvfs: Status 0x%x from AvReadCsvRevisionECP\n", status) ); + + // + // In this case the revision numbers are not available. + // Since there is no way to know if the file was + // changed on another node, it is safest to rescan. + // + needRescanOnCsvfs = TRUE; + } + } + FltReleaseContext( instanceContext ); + } else { + // + // If unable to get instance context then the code isn't sure + // if it is on a CSVFS volume or not. The safest assumption + // would be to rescan the file. + // + needRescanOnCsvfs = TRUE; + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPostCreateCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) ); + } + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvPostCreateCsvfs: %ws need rescan\n", + needRescanOnCsvfs ? L"Does" : L"Does not") + ); + + + // + // if it has been determined that a rescan is needed then set the + // file modified flag on the stream context to indicate this + // + if (needRescanOnCsvfs) { + if ( StreamContext->TxContext != NULL) { + + // + // Instead of updating State, we update TxState here, + // because the file is part of a transaction writer + // + + SET_FILE_TX_MODIFIED( StreamContext ); + + } else { + + SET_FILE_MODIFIED( StreamContext ); + } + + // + // If CSVFS has provided us with valid revison numbers then + // return them to the caller so it can update them in the + // stream context if the scan is successful. + // + if ((VolumeRevision != 0) && + (CacheRevision != 0) && + (FileRevision != 0)) { + *UpdateRevisionNumbers = TRUE; + *VolumeRevisionPtr = VolumeRevision; + *CacheRevisionPtr = CacheRevision; + *FileRevisionPtr = FileRevision; + } + } + + return status; +} + +NTSTATUS +AvPreCleanupCsvfs ( + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _Out_ BOOLEAN *UpdateRevisionNumbers, + _Out_ LONGLONG *VolumeRevisionPtr, + _Out_ LONGLONG *CacheRevisionPtr, + _Out_ LONGLONG *FileRevisionPtr + ) +/*++ + +Routine Description: + + This function implements the PreCleanup processing associated with a + CSVFS volume. The work done is to retrieve the current revision + numbers and determine if a rescan of the file is needed. A recan + would be needed if the revision numbers are not + able to be retrieved, are not valid or do not match the previously + recorded revision numbers. + +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. + + StreamContext - Pointer to the AV stream context + + *UpdateRevisionNumbers returns TRUE if the revision numbers should + be updated in the stream context upon a successful scan + + *VolumeRevisionPtr return with the updated volume revision number + + *CacheRevisionPtr return with the updated cache revision number + + *FileRevisionPtr return with the updated file revision number + +Return Value: + + Status + +--*/ +{ + PAV_INSTANCE_CONTEXT instanceContext = NULL; + NTSTATUS status; + LONGLONG VolumeRevision = 0, CacheRevision = 0, FileRevision = 0; + BOOLEAN needRescanOnCsvfs = FALSE; + + UNREFERENCED_PARAMETER( Data ); + + PAGED_CODE(); + + *UpdateRevisionNumbers = FALSE; + *VolumeRevisionPtr = 0; + *CacheRevisionPtr = 0; + *FileRevisionPtr = 0; + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) { + + // + // If this file is on CSVFS then we cannot completely rely + // upon tracking if the file is modified only on this node, + // but also need to track if the file has been modified on + // any node. So query for the updated revision numbers to + // see if a scan is needed. + // + status = AvQueryCsvRevisionNumbers( FltObjects, + &VolumeRevision, + &CacheRevision, + &FileRevision ); + + if (NT_SUCCESS( status )) { + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + (" [AV] AvPreCleanupCsvfs: %I64x:%I64x:%I64x\n", + VolumeRevision, + CacheRevision, + FileRevision) ); + + // + // It is very possible that the file was changed by + // another node in the cluster and so we need to check + // this case. So if any of the revision number have + // changed we need to assume that the file has changed. + // Note that this is a very pessimistic assumption as + // the Volume and Cache revision numbers could change + // without a corresponding file change but rescanning + // when these change will ensure that a file changed + // on another node will not be opened without being + // rescanned on this node. + // + // Also note that there are cases where the revision + // numbers are zero and thus not at all valid. In this + // case the file must be rescanned as it is not known + // if it was changed or not. + // + needRescanOnCsvfs = ( (VolumeRevision == 0) || + (CacheRevision == 0) || + (FileRevision == 0) || + (VolumeRevision != StreamContext->VolumeRevision) || + (CacheRevision != StreamContext->CacheRevision) || + (FileRevision != StreamContext->FileRevision) ); + } else { + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + (" [AV] AvPreCleanupCsvfs: Status 0x%x from AvReadCsvRevisionECP\n", status) ); + + // + // In this case the revision numbers are not available. + // Since there is no way to know if the file was + // changed on another node, it is safest to rescan. + // + needRescanOnCsvfs = TRUE; + } + } + FltReleaseContext( instanceContext ); + } else { + // + // If unable to get instance context then the code isn't sure + // if it is on a CSVFS volume or not. The safest assumption + // would be to rescan the file. + // + needRescanOnCsvfs = TRUE; + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvPreCleanupCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) ); + } + + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[Av]: AvPreCleanupCsvfs: %ws need rescan\n", + needRescanOnCsvfs ? L"Does" : L"Does not") + ); + + + // + // if it has been determined that a rescan is needed then set the + // file modified flag on the stream context to indicate this + // + if (needRescanOnCsvfs) { + if ( StreamContext->TxContext != NULL) { + + // + // Instead of updating State, we update TxState here, + // because the file is part of a transaction writer + // + + SET_FILE_TX_MODIFIED( StreamContext ); + + } else { + + SET_FILE_MODIFIED( StreamContext ); + } + + // + // If CSVFS has provided us with valid revison numbers then + // return them to the caller so it can update them in the + // stream context if the scan is successful. + // + if ((VolumeRevision != 0) && + (CacheRevision != 0) && + (FileRevision != 0)) { + *UpdateRevisionNumbers = TRUE; + *VolumeRevisionPtr = VolumeRevision; + *CacheRevisionPtr = CacheRevision; + *FileRevisionPtr = FileRevision; + } + } + + return status; +} diff --git a/filesys/miniFilter/avscan/filter/csvfs.h b/filesys/miniFilter/avscan/filter/csvfs.h new file mode 100644 index 00000000..7e34ce3c --- /dev/null +++ b/filesys/miniFilter/avscan/filter/csvfs.h @@ -0,0 +1,62 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + csvfs.h + +Abstract: + + This module contains the scan interface for AV filter to call. + +Environment: + + Kernel mode + +--*/ +#ifndef __CSVFS_H__ +#define __CSVFS_H__ + + +NTSTATUS +AvPreCleanupCsvfs ( + _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _Out_ BOOLEAN *UpdateRevisionNumbers, + _Out_ LONGLONG *VolumeRevisionPtr, + _Out_ LONGLONG *CacheRevisionPtr, + _Out_ LONGLONG *FileRevisionPtr + ); + +NTSTATUS +AvPostCreateCsvfs ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PAV_STREAM_CONTEXT StreamContext, + _Out_ BOOLEAN *UpdateRevisionNumbers, + _Out_ LONGLONG *VolumeRevisionPtr, + _Out_ LONGLONG *CacheRevisionPtr, + _Out_ LONGLONG *FileRevisionPtr + ); + +NTSTATUS +AvPreCreateCsvfs ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects + ); + +BOOLEAN +AvIsCsvDlEcpPresent ( + _In_ PFLT_FILTER Filter, + _In_ PFLT_CALLBACK_DATA Data + ); + +BOOLEAN +AvIsVolumeOnCsvDisk ( + _In_ PFLT_VOLUME Volume + ); + +#endif + diff --git a/filesys/miniFilter/avscan/filter/scan.c b/filesys/miniFilter/avscan/filter/scan.c new file mode 100644 index 00000000..12fe69c3 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/scan.c @@ -0,0 +1,571 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + scan.c + +Abstract: + + This modules wraps the scanning routines. + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" + +// +// Local routines prototypes. +// + +AVSCAN_RESULT +AvScanMemoryStream( + _In_reads_bytes_(Size) PVOID StartingAddress, + _In_ SIZE_T Size, + _In_ PBOOLEAN OperationCanceled + ); + +NTSTATUS +AvMapSectionAndScan( + _Inout_ PAV_SECTION_CONTEXT SectionContext, + _Out_ AVSCAN_RESULT *ScanResult + ); + +// +// Routine implementaions +// + +AVSCAN_RESULT +AvScanMemoryStream( + _In_reads_bytes_(Size) PVOID StartingAddress, + _In_ SIZE_T Size, + _In_ PBOOLEAN OperationCanceled + ) +/*++ + +Routine Description + + A helper function to scan the memory starting at StartingAddress. + This function is only called if the scan mode is AvKernelMode. + +Arguments + + StartingAddress - The starting memory address to be scanned. + + Size - The size of the memory to be scanned. + + OperationCanceled - In the scan loop, it is supposed to poll this flag, + to see if the operation has been canceled. + +Return Value + + The scan result + +--*/ +{ + UCHAR targetString[AV_DEFAULT_SEARCH_PATTERN_SIZE] = {0}; + SIZE_T searchStringLength = AV_DEFAULT_SEARCH_PATTERN_SIZE-1; + ULONG ind; + PUCHAR p; + PUCHAR start = StartingAddress; + PUCHAR end = start + Size - searchStringLength; + + // + // Decode the target pattern. + // + + RtlCopyMemory( (PVOID) targetString, + AV_DEFAULT_SEARCH_PATTERN, + AV_DEFAULT_SEARCH_PATTERN_SIZE ); + + for (ind = 0; + ind < searchStringLength; + ind++) { + + targetString[ind] = ((UCHAR)targetString[ind]) ^ AV_DEFAULT_PATTERN_XOR_KEY; + } + targetString[searchStringLength] = '\0'; + + // + // Scan the memory stream for the target pattern. + // + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[Av]: ASMS: %p, %p, %llu, %llu\n", + start, + end, + Size, + searchStringLength) ); + + for (p = start; p <= end; p++) { + + // if not canceled, continue to search for pattern + if((*OperationCanceled)) { + + return AvScanResultUndetermined; + } + + if (RtlEqualMemory( p, targetString, searchStringLength )) { + + return AvScanResultInfected; + } + } + + *OperationCanceled = FALSE; // Reset the cancel flag, after breaks out the loop. + + return AvScanResultClean; +} + +NTSTATUS +AvMapSectionAndScan( + _Inout_ PAV_SECTION_CONTEXT SectionContext, + _Out_ AVSCAN_RESULT *ScanResult + ) +/*++ + +Routine Description + + A helper function to map the section object and scan the mapped memory. + +Arguments + + SectionContext - Section context containing section object and handle. + + Infected - Return TRUE if the file is infected. + +Return Value + + Returns the status of this operation. + +--*/ +{ + NTSTATUS status; + CLIENT_ID clientId; + OBJECT_ATTRIBUTES objAttribs; + HANDLE processHandle = NULL; + PVOID scanAddress = NULL; + SIZE_T scanSize = 0; + AVSCAN_RESULT scanResult; + + clientId.UniqueThread = PsGetCurrentThreadId(); + clientId.UniqueProcess = PsGetCurrentProcessId(); + + InitializeObjectAttributes(&objAttribs, + NULL, + OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenProcess( &processHandle, + PROCESS_ALL_ACCESS, + &objAttribs, + &clientId ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvMapSectionAndScan: Failed to open the process, 0x%08x\n", + status) ); + goto Cleanup; + } + + status = ZwMapViewOfSection( SectionContext->SectionHandle, + processHandle, + &scanAddress, + 0, + 0, + NULL, + &scanSize, + ViewUnmap, + 0, + PAGE_READONLY ); + if (!NT_SUCCESS(status)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvMapSectionAndScan: Failed to map the view of the section, 0x%08x\n", + status) ); + + goto Cleanup; + } + + // + // The size here may have truncation. + // + scanResult = AvScanMemoryStream( scanAddress, + (SIZE_T)min((LONGLONG)scanSize, SectionContext->FileSize), + &SectionContext->Aborted ); + + *ScanResult = scanResult; + +Cleanup: + + if (scanAddress != NULL) { + + ZwUnmapViewOfSection( processHandle, scanAddress ); + } + + if (processHandle != NULL) { + + ZwClose( processHandle ); + } + + return status; +} + +NTSTATUS +AvScanInKernel ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _In_ PAV_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description + + This function is a high level function which + will do the kernel-mode data scan. + +Arguments + + FltObjects - related objects for the IO operation. + + IOMajorFunctionAtScan - The major function of the IRP that issues this scan. + + IsInTxWriter - If this file is enlisted in a transacted writer. + + StreamContext - The stream context of this data stream. + +Return Value + + Returns the status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES objAttribs; + PAV_SECTION_CONTEXT sectionContext; + AVSCAN_RESULT scanResult = AvScanResultUndetermined; + + status = AvCreateSectionContext( FltObjects->Instance, + FltObjects->FileObject, + §ionContext ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[AV] AvScanInKernel: failed to create section context.\n") ); + + return status; + } + + sectionContext->CancelableOnConflictingIo = (IOMajorFunctionAtScan == IRP_MJ_CLEANUP); + + InitializeObjectAttributes(&objAttribs, + NULL, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = FltCreateSectionForDataScan( FltObjects->Instance, + FltObjects->FileObject, + sectionContext, + SECTION_MAP_READ, + &objAttribs, + NULL, + PAGE_READONLY, + SEC_COMMIT, + 0, + §ionContext->SectionHandle, + §ionContext->SectionObject, + NULL ); + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvScanInKernel: Failed to create section for data scan.\n, 0x%08x\n", + status) ); + return status; + } + + status = AvMapSectionAndScan( sectionContext, &scanResult ); + + if (!NT_SUCCESS( status )) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvScanInKernel: Failed to scan the view of the section.\n, 0x%08x\n", + status) ); + } + + if (scanResult == AvScanResultClean) { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvScanInKernel: file %I64x,%I64x is CLEAN!!\n", + StreamContext->FileId.FileId64.UpperZeroes, + StreamContext->FileId.FileId64.Value) ); + + SET_FILE_NOT_INFECTED_EX( IsInTxWriter, StreamContext ); + + } else if (scanResult == AvScanResultInfected) { + + AV_DBG_PRINT( AVDBG_TRACE_DEBUG, + ("[AV] AvScanInKernel: file %I64x,%I64x is INFECTED!!\n", + StreamContext->FileId.FileId64.UpperZeroes, + StreamContext->FileId.FileId64.Value) ); + + SET_FILE_INFECTED_EX( IsInTxWriter, StreamContext ); + + } else { + + AV_DBG_PRINT( AVDBG_TRACE_ROUTINES, + ("[AV] AvScanInKernel: file %I64x,%I64x is UNKNOWN!!\n", + StreamContext->FileId.FileId64.UpperZeroes, + StreamContext->FileId.FileId64.Value) ); + + SET_FILE_UNKNOWN_EX( IsInTxWriter, StreamContext ); + } + + status = AvFinalizeSectionContext(sectionContext); + + return status; +} + +NTSTATUS +AvScanInUser ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _In_ DEVICE_TYPE DeviceType + ) +/*++ + +Routine Description + + This function is a high level function which + will do the user-mode data scan. + +Arguments + + Data - Pointer to the filter callbackData that is passed to us. + + FltObjects - related objects for the IO operation. + + IOMajorFunctionAtScan - The major function of the IRP that issues this scan. + + IsInTxWriter - If this file is enlisted in a transacted writer. + + StreamContext - The stream context of this data stream. + +Return Value + + Returns the status of this operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG scanThreadId; + ULONG replyLength = sizeof(ULONG); + PAV_SCAN_CONTEXT scanCtx = NULL; + AV_SCANNER_NOTIFICATION notification = {0}; + LONGLONG _1ms = 10000; + LARGE_INTEGER timeout = {0}; + + status = AvAllocateScanContext(FltObjects->Instance, + FltObjects->FileObject, + &scanCtx); + if (!NT_SUCCESS(status)) { + + return status; + } + + // + // Scan context is passed to the user service program. + // Initialize it here. + // + + KeInitializeEvent( &scanCtx->ScanCompleteNotification, NotificationEvent, FALSE ); + scanCtx->IOMajorFunctionAtScan = IOMajorFunctionAtScan; + scanCtx->IsFileInTxWriter = IsInTxWriter; + scanCtx->SectionContext = NULL; + + AvAcquireResourceExclusive( &Globals.ScanCtxListLock ); + if (Globals.Unloading) { + // + // If the filter is being unloaded, we failed the scan. + // + AvReleaseResource( &Globals.ScanCtxListLock ); + AvReleaseScanContext( scanCtx ); + + return STATUS_FLT_DELETING_OBJECT; + } + scanCtx->ScanId = (++Globals.ScanIdCounter); + InsertTailList (&Globals.ScanCtxListHead, &scanCtx->List); + AvReleaseResource( &Globals.ScanCtxListLock ); + + // + // Tell the user-scanner to start to scan the file + // + + notification.Message = AvMsgStartScanning; + notification.ScanId = scanCtx->ScanId; + notification.Reason = AvScanOnOpen; + + if (IOMajorFunctionAtScan == IRP_MJ_CLEANUP) { + notification.Reason = AvScanOnCleanup; + } + + // + // Set the scan timeout for this file based on if it is a local or + // network file. These values can come from the registry. + // + + if (DeviceType == FILE_DEVICE_NETWORK) { + timeout.QuadPart = Globals.NetworkScanTimeout; + } else { + timeout.QuadPart = Globals.LocalScanTimeout; + } + + timeout.QuadPart = -(timeout.QuadPart * _1ms); + + status = FltSendMessage( Globals.Filter, + &Globals.ScanClientPort, + ¬ification, + sizeof(AV_SCANNER_NOTIFICATION), + &scanThreadId, + &replyLength, + &timeout ); + // + // If the message is not delievered or time-out, we can make sure that + // the scanner thread did not acknowledged this scan task, and thus + // we can safely remove it from the list. + // + if (!NT_SUCCESS( status ) || status == STATUS_TIMEOUT) { + + if ((status != STATUS_PORT_DISCONNECTED) && + (status != STATUS_TIMEOUT)) { + + AV_DBG_PRINT( AVDBG_TRACE_ERROR, + ("[Av]: AvScanInUser: Failed to FltSendMessage.\n, 0x%08x\n", + status) ); + } + goto Cleanup; + } + + scanCtx->ScanThreadId = scanThreadId; + + // + // Wait for an event that the scanner completes or aborts. + // + + status = FltCancellableWaitForSingleObject( &scanCtx->ScanCompleteNotification, + &timeout, + Data ); + + if (!NT_SUCCESS(status) || + (status == STATUS_TIMEOUT)) { + + // + // At this point we came out of the wait with an error. We are in one of the following conditions: + // + // 1. This thread is being terminated + // 2. The IO operation represented by Data was cancelled + // 3. If we are in user-mode scan mode, the communication to the user mode component timed out + // 4. If we are in user-mode scan mode, the user-mode component died and the wait timed out. + // + + NTSTATUS statusAbort = STATUS_SUCCESS; + // + // Notify the user scan thread to abort the scan. + // + statusAbort = AvSendAbortToUser(scanCtx->ScanThreadId, + scanCtx->ScanId); + if (NT_SUCCESS(statusAbort) && + (statusAbort != STATUS_TIMEOUT)) { + + LARGE_INTEGER timeoutForAbortComplete = {0}; + timeoutForAbortComplete.QuadPart = - 1000 * (LONGLONG)_1ms; // 1s + // + // Wait again on completion notification. + // The scan thread should close the section very soon because we have already notified + // the scan thread to abort the task. + // + statusAbort = FltCancellableWaitForSingleObject( + &scanCtx->ScanCompleteNotification, + &timeoutForAbortComplete, + NULL ); + } + // + // If send abortion failed or wait failed, which general means the service is dead, + // we have to close section context/handle here by ourself. + // + if (!NT_SUCCESS(statusAbort) || + (statusAbort == STATUS_TIMEOUT)) { + + scanCtx->IoWaitOnScanCompleteNotificationAborted = TRUE; + // + // If this thread who the race, it will close the section. + // + AvFinalizeScanAndSection(scanCtx); + } + } + + // + // If the wait for scan to complete is cancelled (e.g. by CancelSynchronousIo ) + // + if (!NT_SUCCESS(status) && + (IOMajorFunctionAtScan == IRP_MJ_CREATE)) { + + AvCancelFileOpen(Data, FltObjects, status); + } + +Cleanup: + + // + // Here scanCtx must be non-NULL because we checked it in the beginning. + // + + AvAcquireResourceExclusive( &Globals.ScanCtxListLock ); + RemoveEntryList (&scanCtx->List); + AvReleaseResource( &Globals.ScanCtxListLock ); + + AvReleaseScanContext( scanCtx ); + + return status; +} + + +NTSTATUS +AvCloseSectionForDataScan( + _Inout_ PAV_SECTION_CONTEXT SectionContext + ) +/*++ + +Routine Description + + A wrapper function that wraps FltCloseSectionForDataScan and performs appropriate cleanup. + +Arguments + + SectionContext - The seciton handle and object will be cleaned up in sectino context. + +Return Value + + Returns the status of this operation. + +--*/ +{ + // + // Synchronized with AvScanAbortCallbackAsync(...) + // + InterlockedExchangePointer( &SectionContext->ScanContext, NULL ); + ObDereferenceObject( SectionContext->SectionObject ); + + SectionContext->SectionHandle = NULL; + SectionContext->SectionObject = NULL; + return FltCloseSectionForDataScan( (PFLT_CONTEXT)SectionContext ); +} + diff --git a/filesys/miniFilter/avscan/filter/scan.h b/filesys/miniFilter/avscan/filter/scan.h new file mode 100644 index 00000000..5a18abdc --- /dev/null +++ b/filesys/miniFilter/avscan/filter/scan.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + scan.h + +Abstract: + + This module contains the scan interface for AV filter to call. + +Environment: + + Kernel mode + +--*/ +#ifndef __SCAN_H__ +#define __SCAN_H__ + +#include "avlib.h" + +typedef enum _AV_SCAN_MODE { + + // + // AvKernelMode indicates the scanning occurs in the kernel, while + // AvUserMode indicates the scanning happens in the user space. + // + + AvKernelMode, + AvUserMode + +} AV_SCAN_MODE; + +NTSTATUS +AvScanInKernel ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _In_ PAV_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +AvScanInUser ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ UCHAR IOMajorFunctionAtScan, + _In_ BOOLEAN IsInTxWriter, + _In_ DEVICE_TYPE DeviceType + ); + +NTSTATUS +AvCreateSectionForDataScan ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Inout_ PAV_SECTION_CONTEXT SectionContext + ); + +NTSTATUS +AvCloseSectionForDataScan( + _Inout_ PAV_SECTION_CONTEXT SectionContext + ); + +#endif + diff --git a/filesys/miniFilter/avscan/filter/utility.c b/filesys/miniFilter/avscan/filter/utility.c new file mode 100644 index 00000000..7b51a739 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/utility.c @@ -0,0 +1,370 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + utility.c + +Abstract: + + Utility module implementation. + 1) Generic table routines + 2) Query file information routines + +Environment: + + Kernel mode + +--*/ + +#include "avscan.h" + +// +// Generic table routines. +// + +RTL_GENERIC_COMPARE_RESULTS +AvCompareEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ PVOID Lhs, + _In_ PVOID Rhs + ) +/*++ + +Routine Description: + + This routine is the callback for the generic table routines. + +Arguments: + + Table - Table for which this is invoked. + + FirstStruct - An element in the table to compare. + + SecondStruct - Another element in the table to compare. + +Return Value: + + RTL_GENERIC_COMPARE_RESULTS. + +--*/ +{ + PAV_GENERIC_TABLE_ENTRY lhs = (PAV_GENERIC_TABLE_ENTRY)Lhs; + PAV_GENERIC_TABLE_ENTRY rhs = (PAV_GENERIC_TABLE_ENTRY)Rhs; + + UNREFERENCED_PARAMETER (Table); + + // + // Compare the 128 bit fileId in 64bit pieces for efficiency. + // Compare the lower 64 bits Value first since that is used + // in both 128 bit and 64 bit fileIds and doing so eliminates + // and unnecessary comparison of the UpperZeros field in the + // most common case. Note this comparison is not equivalent + // to a memcmp on the 128 bit values but that doesn't matter + // here since we just need the tree to be self-consistent. + // + + if (lhs->FileId.FileId64.Value < rhs->FileId.FileId64.Value) { + + return GenericLessThan; + + } else if (lhs->FileId.FileId64.Value > rhs->FileId.FileId64.Value) { + + return GenericGreaterThan; + + } else if (lhs->FileId.FileId64.UpperZeroes < rhs->FileId.FileId64.UpperZeroes) { + + return GenericLessThan; + + } else if (lhs->FileId.FileId64.UpperZeroes > rhs->FileId.FileId64.UpperZeroes) { + + return GenericGreaterThan; + } + + return GenericEqual; +} + + +PVOID +NTAPI +AvAllocateGenericTableEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ CLONG ByteSize + ) +/*++ + +Routine Description: + + This routine is the callback for allocation for entries in the generic table. + +Arguments: + + Table - Table for which this is invoked. + + ByteSize - Amount of memory to allocate. + +Return Value: + + Pointer to allocated memory if successful, else NULL. + +--*/ +{ + + UNREFERENCED_PARAMETER (Table); + + return ExAllocatePoolWithTag(PagedPool, ByteSize, AV_TABLE_ENTRY_TAG); +} + +VOID +NTAPI +AvFreeGenericTableEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Entry + ) +/*++ + +Routine Description: + + This routine is the callback for releasing memory for entries in the generic + table. + +Arguments: + + Table - Table for which this is invoked. + + Entry - Entry to free. + +Return Value: + + None. + +--*/ +{ + + UNREFERENCED_PARAMETER (Table); + + ExFreePoolWithTag( Entry, AV_TABLE_ENTRY_TAG ); +} + +// +// Query File Information Routines +// + +NTSTATUS +AvGetFileId ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PAV_FILE_REFERENCE FileId + ) +/*++ + +Routine Description: + + This routine obtains the File ID and saves it in the stream context. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + pFileId - Pointer to file id. This is the output + +Return Value: + + Returns statuses forwarded from FltQueryInformationFile. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FLT_FILESYSTEM_TYPE type; + + // + // Querying for FileInternalInformation gives you the file ID. + // + + status = FltGetFileSystemType( Instance, &type ); + + if (NT_SUCCESS( status )) { + + if (type == FLT_FSTYPE_REFS) { + + FILE_ID_INFORMATION fileIdInformation; + + status = FltQueryInformationFile( Instance, + FileObject, + &fileIdInformation, + sizeof(FILE_ID_INFORMATION), + FileIdInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + RtlCopyMemory(&(FileId->FileId128), &(fileIdInformation.FileId), sizeof(FileId->FileId128) ); + } + + } else { + + FILE_INTERNAL_INFORMATION fileInternalInformation; + + status = FltQueryInformationFile( Instance, + FileObject, + &fileInternalInformation, + sizeof(FILE_INTERNAL_INFORMATION), + FileInternalInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + FileId->FileId64.Value = fileInternalInformation.IndexNumber.QuadPart; + FileId->FileId64.UpperZeroes = 0ll; + } + } + } + + return status; +} + +NTSTATUS +AvGetFileSize ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PLONGLONG Size + ) +/*++ + +Routine Description: + + This routine obtains the size. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + Size - Pointer to a LONGLONG indicating the file size. This is the output. + +Return Value: + + Returns statuses forwarded from FltQueryInformationFile. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FILE_STANDARD_INFORMATION standardInfo; + + // + // Querying for FileStandardInformation gives you the offset of EOF. + // + + status = FltQueryInformationFile( Instance, + FileObject, + &standardInfo, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + *Size = standardInfo.EndOfFile.QuadPart; + } + + return status; +} + +NTSTATUS +AvGetFileEncrypted ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PBOOLEAN Encrypted + ) +/*++ + +Routine Description: + + This routine obtains the File ID and saves it in the stream context. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + Encrypted - Pointer to a boolean indicating if this file is encrypted or not. This is the output. + +Return Value: + + Returns statuses forwarded from FltQueryInformationFile. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FILE_BASIC_INFORMATION basicInfo; + + // + // Querying for basic information to get encryption. + // + + status = FltQueryInformationFile( Instance, + FileObject, + &basicInfo, + sizeof(FILE_BASIC_INFORMATION), + FileBasicInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + *Encrypted = BooleanFlagOn( basicInfo.FileAttributes, FILE_ATTRIBUTE_ENCRYPTED ); + } + + return status; +} + +LONG +AvExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ) +/*++ + +Routine Description: + + Exception filter to catch errors touching user buffers. + +Arguments: + + ExceptionPointer - The exception record. + + AccessingUserBuffer - If TRUE, overrides FsRtlIsNtStatusExpected to allow + the caller to munge the error to a desired status. + +Return Value: + + EXCEPTION_EXECUTE_HANDLER - If the exception handler should be run. + + EXCEPTION_CONTINUE_SEARCH - If a higher exception handler should take care of + this exception. + +--*/ +{ + NTSTATUS Status; + + Status = ExceptionPointer->ExceptionRecord->ExceptionCode; + + // + // Certain exceptions shouldn't be dismissed within the filter + // unless we're touching user memory. + // + + if (!FsRtlIsNtstatusExpected( Status ) && + !AccessingUserBuffer) { + + return EXCEPTION_CONTINUE_SEARCH; + } + + return EXCEPTION_EXECUTE_HANDLER; +} + + diff --git a/filesys/miniFilter/avscan/filter/utility.h b/filesys/miniFilter/avscan/filter/utility.h new file mode 100644 index 00000000..97447665 --- /dev/null +++ b/filesys/miniFilter/avscan/filter/utility.h @@ -0,0 +1,252 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + utility.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. The functions include + generic table routines. + +Environment: + + Kernel mode + +--*/ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +#define AV_STRING_TAG 'tSvA' +#define AV_RESOURCE_TAG 'cRvA' +#define AV_KEVENT_TAG 'eKvA' +#define AV_TABLE_ENTRY_TAG 'eTvA' + +////////////////////////////////////////////////////////////////////////////// +// ReFS Compatibility Helpers // +////////////////////////////////////////////////////////////////////////////// + +// +// This helps us deal with ReFS 128-bit file IDs and NTFS 64-bit file IDs. +// + +#define AV_INVALID_FILE_REFERENCE( _fileid_ ) \ + (((_fileid_).FileId64.UpperZeroes == 0ll) && \ + ((_fileid_).FileId64.Value == (ULONGLONG)FILE_INVALID_FILE_ID)) + +#define AV_SET_INVALID_FILE_REFERENCE( _fileid_ ) \ + (_fileid_).FileId64.UpperZeroes = 0ll;\ + (_fileid_).FileId64.Value = (ULONGLONG)FILE_INVALID_FILE_ID; + +typedef union _AV_FILE_REFERENCE { + + struct { + ULONGLONG Value; + ULONGLONG UpperZeroes; + } FileId64; + + FILE_ID_128 FileId128; + +} AV_FILE_REFERENCE, *PAV_FILE_REFERENCE; + + +// +// The generic table entry data structure. +// + +typedef struct _AV_GENERIC_TABLE_ENTRY { + + AV_FILE_REFERENCE FileId; + ULONG InfectedState; + + // + // Revision numbers for files on CSVFS + // + LONGLONG VolumeRevision; + LONGLONG CacheRevision; + LONGLONG FileRevision; + +} AV_GENERIC_TABLE_ENTRY, *PAV_GENERIC_TABLE_ENTRY; + +#define AV_GENERIC_TABLE_ENTRY_SIZE sizeof( AV_GENERIC_TABLE_ENTRY ) + +/* +_IRQL_requires_same_ +_Function_class_(RTL_GENERIC_COMPARE_ROUTINE) +RTL_GENERIC_COMPARE_RESULTS +AvCompareEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ PVOID FirstStruct, + _In_ PVOID SecondStruct + ); + +_IRQL_requires_same_ +__drv_allocatesMem(Mem) +_Function_class_(RTL_GENERIC_ALLOCATE_ROUTINE) +PVOID +NTAPI +AvAllocateGenericTableEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ CLONG ByteSize + ); + +_IRQL_requires_same_ +_Function_class_(RTL_GENERIC_FREE_ROUTINE) +VOID +NTAPI +AvFreeGenericTableEntry ( + _In_ PRTL_GENERIC_TABLE Table, + _In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Entry + ); +*/ + +RTL_GENERIC_COMPARE_ROUTINE AvCompareEntry; + +RTL_GENERIC_ALLOCATE_ROUTINE AvAllocateGenericTableEntry; + +RTL_GENERIC_FREE_ROUTINE AvFreeGenericTableEntry; + +// +// NTFS supports a file state cache. Since CSVFS is built on top of +// NTFS, it can also support the cache. +// +#define FS_SUPPORTS_FILE_STATE_CACHE(VolumeFilesystemType) \ + ( ((VolumeFilesystemType) == FLT_FSTYPE_NTFS) || \ + ((VolumeFilesystemType) == FLT_FSTYPE_CSVFS) || \ + ((VolumeFilesystemType) == FLT_FSTYPE_REFS) ) + + +FORCEINLINE +PERESOURCE +AvAllocateResource ( + VOID + ) +{ + // + // eResource by its rule has to be in the non-paged pool + // NonPagedPoolNx: non-executable non-paged pool + // + + return ExAllocatePoolWithTag( NonPagedPoolNx, + sizeof( ERESOURCE ), + AV_RESOURCE_TAG ); +} + +FORCEINLINE +VOID +AvFreeResource ( + _In_ PERESOURCE Resource + ) +{ + + ExFreePoolWithTag( Resource, + AV_RESOURCE_TAG ); +} + +FORCEINLINE +PKEVENT +AvAllocateKevent ( + VOID + ) +{ + // + // KEVENT has to be in the non-paged pool + // + + return ExAllocatePoolWithTag( NonPagedPoolNx, + sizeof( KEVENT ), + AV_KEVENT_TAG ); +} + +FORCEINLINE +VOID +AvFreeKevent ( + _In_ PKEVENT Event + ) +{ + + ExFreePoolWithTag( Event, + AV_KEVENT_TAG ); +} + +NTSTATUS +AvGetFileId ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PAV_FILE_REFERENCE FileId + ); + +NTSTATUS +AvGetFileSize ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PLONGLONG Size + ); + +NTSTATUS +AvGetFileEncrypted ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PBOOLEAN Encrypted + ); + +LONG +AvExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ); + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +AvAcquireResourceExclusive ( + _Inout_ _Acquires_exclusive_lock_(*Resource) PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + !ExIsResourceAcquiredSharedLite(Resource)); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +AvAcquireResourceShared ( + _Inout_ _Acquires_shared_lock_(*Resource) PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceSharedLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(_Global_critical_region_) +AvReleaseResource ( + _Inout_ _Requires_lock_held_(*Resource) _Releases_lock_(*Resource) PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + ExIsResourceAcquiredSharedLite(Resource)); + + ExReleaseResourceLite(Resource); + KeLeaveCriticalRegion(); +} + +#define LIST_FOR_EACH_SAFE(curr, n, head) \ + for (curr = (head)->Flink , n = curr->Flink ; curr != (head); \ + curr = n, n = curr->Flink ) + +#endif + diff --git a/filesys/miniFilter/avscan/inc/avlib.h b/filesys/miniFilter/avscan/inc/avlib.h new file mode 100644 index 00000000..a5d5ece6 --- /dev/null +++ b/filesys/miniFilter/avscan/inc/avlib.h @@ -0,0 +1,201 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + avlib.h + +Abstract: + + This header file defines the common data structure used by kernel and user. + +Environment: + + User mode + Kernel mode + +--*/ + +#ifndef __AVLIB_H__ +#define __AVLIB_H__ + +#if defined(_MSC_VER) +#if (_MSC_VER >= 1200) +#pragma warning(push) +#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union +#endif +#endif + +// +// Name of AV filter server ports +// + +#define AV_SCAN_PORT_NAME L"\\MicrosoftAvSampleFilterScanPort" +#define AV_ABORT_PORT_NAME L"\\MicrosoftAvSampleFilterAbortPort" +#define AV_QUERY_PORT_NAME L"\\MicrosoftAvSampleFilterQueryPort" + + +// +// Definition of invalide section handle for data scan +// + +#define AV_INVALID_SECTION_HANDLE ((HANDLE)((LONG_PTR)(-1))) + + +// +// Command type enumeration, please see COMMAND_MESSAGE below +// + +typedef enum _AVSCAN_COMMAND { + + AvIsFileModified, + AvCmdCreateSectionForDataScan, + AvCmdCloseSectionForDataScan + +} AVSCAN_COMMAND; + +// +// Message type enumeration, please see AV_SCANNER_NOTIFICATION below +// + +typedef enum _AVSCAN_MESSAGE { + + AvMsgStartScanning, + AvMsgAbortScanning, + AvMsgFilterUnloading + +} AVSCAN_MESSAGE; + +typedef enum _AVSCAN_REASON { + AvScanOnOpen, + AvScanOnCleanup + +} AVSCAN_REASON; + +typedef enum _AVSCAN_RESULT { + + AvScanResultUndetermined, + AvScanResultInfected, + AvScanResultClean + +} AVSCAN_RESULT; + +// +// Defines the commands between the user program and the filter +// Command: User -> Kernel +// + +typedef struct _COMMAND_MESSAGE { + + // + // Command type + // + + AVSCAN_COMMAND Command; + + // + // Scan identifier. + // This argument will be checked in message notificaiton callback. + // + + LONGLONG ScanId; + + // + // Scan thread id. This id will be used in cancel message passing. + // So that we will know which scan thread to cancel. + // + + ULONG ScanThreadId; + + union { + + // + // When user program is connecting for query (AvConnectForQuery) + // it has to pass the file handle to query the status of the file. + // Valid when Command == AvIsFileModified + // + + HANDLE FileHandle; + + // + // The result result. + // Valid when Command == AvCmdCloseSectionForDataScan + // + AVSCAN_RESULT ScanResult; + }; + +} COMMAND_MESSAGE, *PCOMMAND_MESSAGE; + +// +// Message: Kernel -> User Message +// + +typedef struct _SCANNER_NOTIFICATION { + + // + // Message type + // + + AVSCAN_MESSAGE Message; + + // + // Reason + // + + AVSCAN_REASON Reason; + + // + // Scan identifier. + // This argument will be checked in message notificaiton callback. + // + + LONGLONG ScanId; + + // + // Scan thread id. This id will be used in cancel message passing. + // So that we will know which scan thread to cancel. + // + + ULONG ScanThreadId; + +} AV_SCANNER_NOTIFICATION, *PAV_SCANNER_NOTIFICATION; + +// +// Connection type enumeration. It would be mainly used in connection context. +// + +typedef enum _AVSCAN_CONNECTION_TYPE { + + AvConnectForScan = 1, + AvConnectForAbort, + AvConnectForQuery + +} AVSCAN_CONNECTION_TYPE, *PAVSCAN_CONNECTION_TYPE; + +// +// Connection context. It will be passed through FilterConnectCommunicationPort(...) +// + +typedef struct _AV_CONNECTION_CONTEXT { + + AVSCAN_CONNECTION_TYPE Type; + +} AV_CONNECTION_CONTEXT, *PAV_CONNECTION_CONTEXT; + +// +// The following string is actully "message to be found" +// + +#define AV_DEFAULT_SEARCH_PATTERN "7?));=?z.5z8?z<5/4>" +#define AV_DEFAULT_SEARCH_PATTERN_SIZE sizeof(AV_DEFAULT_SEARCH_PATTERN) +#define AV_DEFAULT_PATTERN_XOR_KEY 90 + +#if defined(_MSC_VER) +#if (_MSC_VER >= 1200) +#pragma warning(pop) +#endif +#endif + +#endif + diff --git a/filesys/miniFilter/avscan/user/avscan.c b/filesys/miniFilter/avscan/user/avscan.c new file mode 100644 index 00000000..1b0e7b57 --- /dev/null +++ b/filesys/miniFilter/avscan/user/avscan.c @@ -0,0 +1,101 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + avscan.c + +Abstract: + + The user space anti-virus scanner. It is the entry point of + the user program. + + In its initialization, it forks scan listening threads and + run a couple of unit tests and wait for a user input. + + Before the user types 'q' to quit this program, the scan + threads will continue to work. + +Environment: + + User mode + +--*/ + +#include <windows.h> +#include <stdio.h> +#include <fltUser.h> +#include "utility.h" +#include "avlib.h" +#include "userscan.h" + +int _cdecl +main ( + _Unreferenced_parameter_ int argc, + _Unreferenced_parameter_ char *argv[] + ) +/*++ + +Routine Description: + + Entry main function of the user space program. + +Arguments: + + argc - The number of arguments + argv - The arguments + +Return Value: + + 0 - No error occurs. + 255 - Error occurs. + +--*/ +{ + + UCHAR c; + HRESULT hr = S_OK; + USER_SCAN_CONTEXT userScanCtx = {0}; + + UNREFERENCED_PARAMETER( argc ); + UNREFERENCED_PARAMETER( argv ); + + + // + // Initialize scan listening threads. + // + + hr = UserScanInit(&userScanCtx); + if (FAILED(hr)) { + fprintf(stderr, "Failed to initialize user scan data\n"); + DisplayError( hr ); + return 255; + } + + // + // Read user's input until it reads 'q' + // + + for(;;) { + + printf("press 'q' to quit: "); + c = (unsigned char) getchar(); + if (c == 'q') { + + break; + } + } + + // + // Finalize the scan thread contexts. + // + + hr = UserScanFinalize(&userScanCtx); + if (FAILED(hr)) { + fprintf(stderr, "Failed to finalize the user scan data.\n"); + } + + return 0; +} + diff --git a/filesys/miniFilter/avscan/user/avscan.rc b/filesys/miniFilter/avscan/user/avscan.rc new file mode 100644 index 00000000..800467c8 --- /dev/null +++ b/filesys/miniFilter/avscan/user/avscan.rc @@ -0,0 +1,10 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "AvScan User Program" +#define VER_INTERNALNAME_STR "avscan.exe" +#define VER_ORIGINALFILENAME_STR "avscan.exe" + + diff --git a/filesys/miniFilter/avscan/user/avscan.vcxproj b/filesys/miniFilter/avscan/user/avscan.vcxproj new file mode 100644 index 00000000..7a3112b0 --- /dev/null +++ b/filesys/miniFilter/avscan/user/avscan.vcxproj @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{23D46B81-CF8D-48E5-BF28-3679E6106D7F}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{0D25D92A-41FF-4D6F-A937-A24EE3010777}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>avscan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>avscan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>avscan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>avscan</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="avscan.c" /> + <ClCompile Include="userscan.c" /> + <ClCompile Include="utility.c" /> + <ResourceCompile Include="avscan.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters b/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters new file mode 100644 index 00000000..5ca0df68 --- /dev/null +++ b/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{7C20F6A5-EEFE-4E1E-ACA6-D81A9175944F}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{A175AC1D-BDB6-4F3C-B19E-42160282E929}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{B2375B4F-9F4C-4FA5-9C19-76A963F2A7DA}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="avscan.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="userscan.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="utility.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="avscan.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/avscan/user/userscan.c b/filesys/miniFilter/avscan/user/userscan.c new file mode 100644 index 00000000..71527823 --- /dev/null +++ b/filesys/miniFilter/avscan/user/userscan.c @@ -0,0 +1,1250 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + userscan.c + +Abstract: + + The implementation of user space scanning module. You have to install filter driver first, and + have filter manager load the minifilter. When filter driver is in its position, after calling + UserScanInit(...) all the subsequent CreateFile or CloseHandle would trigger the data scan if + the file is dirty. + + Before the user space scanner exit, it must call UserScanFinalize(...) to cleanup the data structure + and close the listening threads. + +Environment: + + User mode + +--*/ + +#include <stdio.h> +#include <assert.h> +#include "userscan.h" +#include "utility.h" + +#define USER_SCAN_THREAD_COUNT 6 // the number of scanning worker threads. + +typedef struct _SCANNER_MESSAGE { + + // + // Required structure header. + // + + FILTER_MESSAGE_HEADER MessageHeader; + + // + // Private scanner-specific fields begin here. + // + + AV_SCANNER_NOTIFICATION Notification; + + // + // Overlapped structure: this is not really part of the message + // However we embed it here so that when we get pOvlp in + // GetQueuedCompletionStatus(...), we can restore the message + // via CONTAINING_RECORD macro. + // + + OVERLAPPED Ovlp; + +} SCANNER_MESSAGE, *PSCANNER_MESSAGE; + +#define SCANNER_MESSAGE_SIZE (sizeof(FILTER_MESSAGE_HEADER) + sizeof(AV_SCANNER_NOTIFICATION)) + +typedef struct _SCANNER_REPLY_MESSAGE { + + // + // Required structure header. + // + + FILTER_REPLY_HEADER ReplyHeader; + + // + // Private scanner-specific fields begin here. + // + + ULONG ThreadId; + +} SCANNER_REPLY_MESSAGE, *PSCANNER_REPLY_MESSAGE; + +#define SCANNER_REPLY_MESSAGE_SIZE (sizeof(FILTER_REPLY_HEADER) + sizeof(ULONG)) + +// +// Local routines +// + +AVSCAN_RESULT +UserScanMemoryStream( + _In_reads_bytes_(Size) PUCHAR StartingAddress, + _In_ SIZE_T Size, + _Inout_ PBOOLEAN pAbort + ); + +HRESULT +UserScanHandleStartScanMsg( + _In_ PUSER_SCAN_CONTEXT Context, + _In_ PSCANNER_MESSAGE Message, + _In_ PSCANNER_THREAD_CONTEXT ThreadCtx + ); + +HRESULT +UserScanWorker ( + _Inout_ PUSER_SCAN_CONTEXT Context + ); + +HRESULT +UserScanListenAbortProc ( + _Inout_ PUSER_SCAN_CONTEXT Context + ); + +DWORD +WaitForAll ( + _In_ PSCANNER_THREAD_CONTEXT ScanThreadCtxes + ); + +HRESULT +UserScanGetThreadContextById ( + _In_ DWORD ThreadId, + _In_ PUSER_SCAN_CONTEXT Context, + _Out_ PSCANNER_THREAD_CONTEXT *ScanThreadCtx + ); + +VOID +UserScanSynchronizedCancel ( + _In_ PUSER_SCAN_CONTEXT Context + ); + +HRESULT +UserScanClosePorts ( + _In_ PUSER_SCAN_CONTEXT Context + ); + +HRESULT +UserScanCleanup ( + _In_ PUSER_SCAN_CONTEXT Context + ); + +// +// Implementation of exported routines. +// Declared in userscan.h +// + +HRESULT +UserScanInit ( + _Inout_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine initializes all the necessary data structures and forks listening threads. + The caller thread is responsible for calling UserScanFinalize(...) to cleanup the + data structures and close the listening threads. + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + S_OK if successful. Otherwise, it returns a HRESULT error value. + +--*/ +{ + HRESULT hr = S_OK; + ULONG i = 0; + HANDLE hEvent = NULL; + PSCANNER_THREAD_CONTEXT scanThreadCtxes = NULL; + HANDLE hListenAbort = NULL; + AV_CONNECTION_CONTEXT connectionCtx = {0}; + + if (NULL == Context) { + + return MAKE_HRESULT(SEVERITY_ERROR, 0, E_POINTER); + } + + // + // Create the abort listening thead. + // This thread is particularly listening the abortion event. + // + + hListenAbort = CreateThread( NULL, + 0, + (LPTHREAD_START_ROUTINE)UserScanListenAbortProc, + Context, + CREATE_SUSPENDED, + NULL ); + + if (NULL == hListenAbort) { + + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + + // + // Initialize scan thread contexts. + // + + scanThreadCtxes = HeapAlloc(GetProcessHeap(), 0, sizeof(SCANNER_THREAD_CONTEXT) * USER_SCAN_THREAD_COUNT); + if (NULL == scanThreadCtxes) { + + hr = MAKE_HRESULT(SEVERITY_ERROR, 0, E_OUTOFMEMORY); + goto Cleanup; + } + + ZeroMemory(scanThreadCtxes, sizeof(SCANNER_THREAD_CONTEXT) * USER_SCAN_THREAD_COUNT); + + // + // Create scan listening threads. + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + scanThreadCtxes[i].Handle = CreateThread( NULL, + 0, + (LPTHREAD_START_ROUTINE)UserScanWorker, + Context, + CREATE_SUSPENDED, + &scanThreadCtxes[i].ThreadId ); + + if (NULL == scanThreadCtxes[i].Handle) { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + InitializeCriticalSection(&(scanThreadCtxes[i].Lock)); + } + + // + // Prepare the scan communication port. + // + + connectionCtx.Type = AvConnectForScan; + hr = FilterConnectCommunicationPort( AV_SCAN_PORT_NAME, + 0, + &connectionCtx, + sizeof(AV_CONNECTION_CONTEXT), + NULL, + &Context->ConnectionPort ); + if (FAILED(hr)) { + + Context->ConnectionPort = NULL; + goto Cleanup; + } + + // + // Create the IO completion port for asynchronous message passing. + // + + Context->Completion = CreateIoCompletionPort( Context->ConnectionPort, + NULL, + 0, + USER_SCAN_THREAD_COUNT ); + + if ( NULL == Context->Completion ) { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + + Context->ScanThreadCtxes = scanThreadCtxes; + Context->AbortThreadHandle = hListenAbort; + + // + // Resume all the scanning threads. + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + if ( ResumeThread( scanThreadCtxes[i].Handle ) == -1) { + + fprintf(stderr, "[UserScanInit]: ResumeThread scan listening thread failed.\n"); + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + } + + // + // Resume abort listening thread. + // + + if ( ResumeThread( hListenAbort ) == -1 ) { + fprintf(stderr, "[UserScanInit]: ResumeThread abort listening thread failed.\n"); + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Cleanup; + } + + // + // Pump messages into queue of completion port. + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + PSCANNER_MESSAGE msg = HeapAlloc( GetProcessHeap(), 0, sizeof( SCANNER_MESSAGE ) ); + + if (NULL == msg) { + + hr = MAKE_HRESULT(SEVERITY_ERROR, 0, E_OUTOFMEMORY); + goto Cleanup; + } + + FillMemory( &msg->Ovlp, sizeof(OVERLAPPED), 0); + hr = FilterGetMessage( Context->ConnectionPort, + &msg->MessageHeader, + FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ), + &msg->Ovlp ); + + if (hr == HRESULT_FROM_WIN32( ERROR_IO_PENDING )) { + + hr = S_OK; + + } else { + + fprintf(stderr, "[UserScanInit]: FilterGetMessage failed.\n"); + DisplayError(hr); + HeapFree(GetProcessHeap(), 0, msg ); + goto Cleanup; + } + } + + return hr; + +Cleanup: + + if (Context->Completion && !CloseHandle(Context->Completion)) { + + fprintf(stderr, "[UserScanInit] Error! Close completion port failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + if (Context->ConnectionPort && !CloseHandle(Context->ConnectionPort)) { + + fprintf(stderr, "[UserScanInit] Error! Close connection port failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + if (scanThreadCtxes) { + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + if (scanThreadCtxes[i].Handle && !CloseHandle(scanThreadCtxes[i].Handle)) { + + fprintf(stderr, "[UserScanInit] Error! Close scan thread failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + DeleteCriticalSection(&(scanThreadCtxes[i].Lock)); + } + HeapFree(GetProcessHeap(), 0, scanThreadCtxes); + } + if (hListenAbort && !CloseHandle(hListenAbort)) { + + fprintf(stderr, "[UserScanInit] Error! Close listen abort thread failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + if (hEvent && !CloseHandle(hEvent)) { + + fprintf(stderr, "[UserScanInit] Error! Close event handle failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + + return hr; +} + +HRESULT +UserScanFinalize ( + _In_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine cleans up all the necessary data structures and closes listening threads. + It does the following things: + 1) Cancel all the scanning threads and wait for them to terminate. + 2) Close all the thread handles + 3) Close all the port handles + 4) Free memory of scan thread contexts. + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + S_OK if successful. Otherwise, it returns a HRESULT error value. + +--*/ +{ + HRESULT hr = S_OK; + printf("=================finalize\n"); + + UserScanSynchronizedCancel( Context ); + + printf("[UserScanFinalize]: Closing connection port\n"); + + hr = UserScanCleanup( Context ); + + return hr; +} + + +// +// Implementation of local routines +// + +DWORD +WaitForAll ( + _In_ PSCANNER_THREAD_CONTEXT ScanThreadCtxes + ) +/*++ + +Routine Description: + + A local helper function that enable the caller to wair for all the scan threads. + +Arguments: + + ScanThreadCtxes - Scan thread contextes. + +Return Value: + + Please consult WaitForMultipleObjects(...) + +--*/ +{ + ULONG i = 0; + HANDLE hScanThreads[USER_SCAN_THREAD_COUNT] = {0}; + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + hScanThreads[i] = ScanThreadCtxes[i].Handle; + } + return WaitForMultipleObjects(USER_SCAN_THREAD_COUNT, hScanThreads, TRUE, INFINITE); +} + +HRESULT +UserScanGetThreadContextById ( + _In_ DWORD ThreadId, + _In_ PUSER_SCAN_CONTEXT Context, + _Out_ PSCANNER_THREAD_CONTEXT *ScanThreadCtx + ) +/*++ + +Routine Description: + + This routine search for the scan thread context by its thread id. + +Arguments: + + ThreadId - The thread id to be searched. + + Context - The user scan context. + + ScanThreadCtx - Output scan thread context. + +Return Value: + + S_OK if found, otherwise not found. + +--*/ +{ + HRESULT hr = S_OK; + ULONG i; + PSCANNER_THREAD_CONTEXT scanThreadCtx = Context->ScanThreadCtxes; + + *ScanThreadCtx = NULL; + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + if ( ThreadId == scanThreadCtx[i].ThreadId ) { + *ScanThreadCtx = (scanThreadCtx + i); + return hr; + } + } + return MAKE_HRESULT(SEVERITY_ERROR,0,E_FAIL); +} + +VOID +UserScanSynchronizedCancel ( + _In_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine tries to abort all the scanning threads and wait for them to terminate. + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + Please consult WaitForMultipleObjects(...) + +--*/ +{ + ULONG i; + PSCANNER_THREAD_CONTEXT scanThreadCtxes = Context->ScanThreadCtxes; + + if (NULL == scanThreadCtxes) { + fprintf(stderr, "Scan thread contexes are NOT suppoed to be NULL.\n"); + return; + } + + // + // Tell all scanning threads that the program is going to exit. + // + + Context->Finalized = TRUE; + + // + // Signal cancellation events for all scanning threads. + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + scanThreadCtxes[i].Aborted = TRUE; + } + + // + // Wake up the listening thread if it is waiting for message + // via GetQueuedCompletionStatus() + // + + CancelIoEx(Context->ConnectionPort, NULL); + + // + // Wait for all scan threads to complete cancellation, + // so we will be able to close the connection port and etc. + // + + WaitForAll(scanThreadCtxes); + + return; +} + +HRESULT +UserScanClosePorts ( + _In_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine cleans up all the necessary data structures and closes listening threads. + It does closing the scanning communication port and completion port. + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + S_OK if successful. Otherwise, it returns a HRESULT error value. + +--*/ +{ + HRESULT hr = S_OK; + if (!CloseHandle(Context->ConnectionPort)) { + fprintf(stderr, "[UserScanFinalize]: Failed to close the connection port.\n"); + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + Context->ConnectionPort = NULL; + + if (!CloseHandle(Context->Completion)) { + fprintf(stderr, "[UserScanFinalize]: Failed to close the completion port.\n"); + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + Context->Completion = NULL; + + return hr; +} + +HRESULT +UserScanCleanup ( + _In_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine cleans up all the necessary data structures and closes listening threads. + It does closing abort thread handle and all scanning threads. It also closes the ports + by calling UserScanClosePorts(...). + +Arguments: + + Context - User scan context, please see userscan.h + +Return Value: + + S_OK if successful. Otherwise, it returns a HRESULT error value. + +--*/ +{ + ULONG i = 0; + HRESULT hr = S_OK; + PSCANNER_THREAD_CONTEXT scanThreadCtxes = Context->ScanThreadCtxes; + + if (NULL == scanThreadCtxes) { + + fprintf(stderr, "Scan thread contexes are NOT suppoed to be NULL.\n"); + return E_POINTER; + } + + if (Context->AbortThreadHandle) { + + CloseHandle( Context->AbortThreadHandle ); + } + + hr = UserScanClosePorts( Context ); + + // + // Clean up scan thread contexts + // + + for (i = 0; + i < USER_SCAN_THREAD_COUNT; + i ++ ) { + + if (scanThreadCtxes[i].Handle && !CloseHandle(scanThreadCtxes[i].Handle)) { + fprintf(stderr, "[UserScanInit] Error! Close scan thread failed.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + DeleteCriticalSection(&(scanThreadCtxes[i].Lock)); + } + HeapFree( GetProcessHeap(), 0, scanThreadCtxes ); + Context->ScanThreadCtxes = NULL; + return hr; +} + +AVSCAN_RESULT +UserScanMemoryStream( + _In_reads_bytes_(Size) PUCHAR StartingAddress, + _In_ SIZE_T Size, + _Inout_ PBOOLEAN pAbort + ) +/*++ + +Routine Description: + + This routine is a naive search for virus signiture. + Note that this function is by no means efficient and scalable, but + this is not the focus of this example. Thus, an anti-virus + vendor may want to focus on and expand this function. + + It will reset the abort flag if it is aborted. + +Arguments: + + StartingAddress - The starting address of the memory to be searched. + + Size - The size of the memory. + + pAbort - A pointer to a boolean that notifies the scanning should be canceled.. + + Infected - TRUE if this file is infected. FALSE, otherwise. + +Return Value: + + S_OK. + +--*/ +{ + ULONG i; + UCHAR targetString[AV_DEFAULT_SEARCH_PATTERN_SIZE] = {0}; + SIZE_T searchStringLength = AV_DEFAULT_SEARCH_PATTERN_SIZE-1; + ULONG ind; + PUCHAR p; + PUCHAR start = StartingAddress; + PUCHAR end = start + Size - searchStringLength; + + // + // Decode the target pattern. We could decode only once and cache it. + // + + CopyMemory( (PVOID) targetString, + AV_DEFAULT_SEARCH_PATTERN, + AV_DEFAULT_SEARCH_PATTERN_SIZE ); + + for (ind = 0; + ind < searchStringLength; + ind++) { + + targetString[ind] = ((UCHAR)targetString[ind]) ^ AV_DEFAULT_PATTERN_XOR_KEY; + } + targetString[searchStringLength] = '\0'; + + // + // Scan the memory stream for the target pattern. + // If not cancelled. + // + + for (p = start, i = 1; + p <= end ; + p++, i++) { + + // + // If (*pAbort == TRUE), then we abort the scanning in the loop. + // + + if ( *pAbort ) { + + *pAbort = FALSE; + return AvScanResultUndetermined; + } + + if ( !memcmp( p, targetString, searchStringLength )) { + + return AvScanResultInfected; + } + } + + return AvScanResultClean; +} + +HRESULT +UserScanHandleStartScanMsg( + _In_ PUSER_SCAN_CONTEXT Context, + _In_ PSCANNER_MESSAGE Message, + _In_ PSCANNER_THREAD_CONTEXT ThreadCtx + ) +/*++ + +Routine Description: + + After receiving the scan request from the kernel. This routine is + the main function that handle the scan request. + + This routine does not know which file it is scanning because it + does not need to know. + + Its main job includes: + + 1) Send message to the filter to create a section object. + 2) Map the view of the section. + 3) Scan the memory + 4) Send message to tell the filter the result of the scan + and close the section object. + +Arguments: + + Context - The user scan context. + + Message - The message recieved from the kernel. + + ThreadCtx - The scan thread context. + +Return Value: + + S_OK. + +--*/ +{ + HRESULT hr = S_OK; + ULONG bytesReturned = 0; + HANDLE sectionHandle = NULL; + DWORD dwErrCode = 0; + PVOID scanAddress = NULL; + MEMORY_BASIC_INFORMATION memoryInfo; + PAV_SCANNER_NOTIFICATION notification = &Message->Notification; + COMMAND_MESSAGE commandMessage = {0}; + DWORD flags = 0; + + // + // Send the message to the filter to create a section object for data scan. + // If success, we would get section handle. + // + // We just have to transparently pass ScanContextId to filter, which we + // obtained from the filter previously. + // + + commandMessage.Command = AvCmdCreateSectionForDataScan; + commandMessage.ScanId = notification->ScanId; + commandMessage.ScanThreadId = ThreadCtx->ThreadId; + + hr = FilterSendMessage( Context->ConnectionPort, + &commandMessage, + sizeof( COMMAND_MESSAGE ), + §ionHandle, + sizeof( HANDLE ), + &bytesReturned ); + + if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanHandleStartScanMsg]: Failed to send message SendMessageToCreateSection to the minifilter.\n"); + DisplayError(hr); + return hr; + } + + scanAddress = MapViewOfFile( sectionHandle, + FILE_MAP_READ, + 0L, + 0L, + 0 ); + if (scanAddress == NULL) { + fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to map the view.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + goto Cleanup; + } + + if( !VirtualQuery( scanAddress, &memoryInfo, sizeof(memoryInfo) )) { + fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to query the view.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + goto Cleanup; + } + + // + // Data scan here. + // + + commandMessage.ScanResult = UserScanMemoryStream( (PUCHAR)scanAddress, + memoryInfo.RegionSize, + &ThreadCtx->Aborted ); + + // + // If scanning on file open, give the pages a transient boost + // since they may soon be accessed in read operations on the + // file. + // + + if (notification->Reason == AvScanOnOpen) { + flags = MEM_UNMAP_WITH_TRANSIENT_BOOST; + } + +Cleanup: + + if (scanAddress != NULL) { + + if (!UnmapViewOfFileEx( scanAddress, flags )) { + + fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to unmap the view.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + } + + // + // We have to close the section handle after we finish using it. + // It is required to close the section handle here in user mode. + // + + if (!CloseHandle(sectionHandle)) { + + fprintf(stderr, "[UserScanHandleStartScanMsg]: Failed to close the section handle.\n"); + DisplayError(HRESULT_FROM_WIN32(dwErrCode)); + } + + // + // Send the message to tell filter to close the section object. + // This call will set the file clean or infected depending on the scan result, and + // also trigger events and release the waiting I/O request thread. + // + + commandMessage.Command = AvCmdCloseSectionForDataScan; + hr = FilterSendMessage( Context->ConnectionPort, + &commandMessage, + sizeof( COMMAND_MESSAGE ), + NULL, + 0, + &bytesReturned ); + if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanHandleStartScanMsg]: Failed to close message SendMessageToCreateSection to the minifilter.\n"); + DisplayError( hr ); + return hr; + } + + return hr; +} + +HRESULT +UserScanWorker ( + _Inout_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine is the scanning worker thread procedure. + The pseudo-code of this function is as follows, + + while(TRUE) { + 1) Get a overlap structure from the completion port. + 2) Obtain message from overlap structure. + 3) Process the message via calling UserScanHandleStartScanMsg(...) + 4) Pump overlap structure into completion port using FilterGetMessage(...) + } + +Arguments: + + Context - The user scan context. + +Return Value: + + S_OK if no error occurs; Otherwise, it would return appropriate HRESULT. + +--*/ +{ + HRESULT hr = S_OK; + + PSCANNER_MESSAGE message = NULL; + SCANNER_REPLY_MESSAGE replyMsg; + LPOVERLAPPED pOvlp = NULL; + + DWORD outSize; + ULONG_PTR key; + BOOL success = FALSE; + + PSCANNER_THREAD_CONTEXT threadCtx = NULL; + + hr = UserScanGetThreadContextById( GetCurrentThreadId(), Context, &threadCtx ); + if (FAILED(hr)) { + fprintf(stderr, + "[UserScanWorker]: Failed to get thread context.\n"); + return hr; + } + + ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE ); + + printf("Current thread handle %p, id:%u\n", threadCtx->Handle, threadCtx->ThreadId); + + // + // This thread is waiting for scan message from the driver + // + + for(;;) { + + message = NULL; + + // + // Get overlapped structure asynchronously, the overlapped structure + // was previously pumped by FilterGetMessage(...) + // + + success = GetQueuedCompletionStatus( Context->Completion, &outSize, &key, &pOvlp, INFINITE ); + + if (!success) { + + hr = HRESULT_FROM_WIN32(GetLastError()); + + // + // The completion port handle associated with it is closed + // while the call is outstanding, the function returns FALSE, + // *lpOverlapped will be NULL, and GetLastError will return ERROR_ABANDONED_WAIT_0 + // + + if (hr == E_HANDLE) { + + printf("Completion port becomes unavailable.\n"); + hr = S_OK; + + } else if (hr == HRESULT_FROM_WIN32(ERROR_ABANDONED_WAIT_0)) { + + printf("Completion port was closed.\n"); + hr = S_OK; + } + + break; + } + + // + // Recover message strcuture from overlapped structure. + // Remember we embedded overlapped structure inside SCANNER_MESSAGE. + // This is because the overlapped structure obtained from GetQueuedCompletionStatus(...) + // is asynchronously and not guranteed in order. + // + + message = CONTAINING_RECORD( pOvlp, SCANNER_MESSAGE, Ovlp ); + + if (AvMsgStartScanning == message->Notification.Message) { + + // + // Reset the abort flag since this is a new scan request and remember + // the scan context ID. This ID will allow us to match a cancel request + // with a given scan task. + // + + EnterCriticalSection(&(threadCtx->Lock)); + threadCtx->Aborted = FALSE; + threadCtx->ScanId = message->Notification.ScanId; + LeaveCriticalSection(&(threadCtx->Lock)); + + // + // Reply the scanning worker thread handle to the filter + // This is important because the filter will also wait for the scanning thread + // in case that the scanning thread is killed before telling filter + // the scan is done or aborted. + // + + ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE ); + replyMsg.ReplyHeader.MessageId = message->MessageHeader.MessageId; + replyMsg.ThreadId = threadCtx->ThreadId; + hr = FilterReplyMessage( Context->ConnectionPort, + &replyMsg.ReplyHeader, + SCANNER_REPLY_MESSAGE_SIZE ); + + if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanWorker]: Failed to reply thread handle to the minifilter\n"); + DisplayError(hr); + break; + } + hr = UserScanHandleStartScanMsg( Context, message, threadCtx ); + + } else { + + assert( FALSE ); // This thread should not receive other kinds of message. + } + + + if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanWorker]: Failed to handle the message.\n"); + } + + // + // If fianlized flag is set from main thread, + // then it would break the while loop. + // + + if (Context->Finalized) { + + break; + } + + // + // After we process the message, pump a overlapped structure into completion port again. + // + + hr = FilterGetMessage( Context->ConnectionPort, + &message->MessageHeader, + FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ), + &message->Ovlp ); + + if (hr == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)) { + + printf("FilterGetMessage aborted.\n"); + break; + + } else if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) { + + fprintf(stderr, + "[UserScanWorker]: Failed to get message from the minifilter. \n0x%x, 0x%x\n", + hr, HRESULT_FROM_WIN32(GetLastError())); + DisplayError(hr); + break; + } + + } // end of while(TRUE) + + if (message) { + + // + // Free the memory, which originally allocated at UserScanInit(...) + // + + HeapFree(GetProcessHeap(), 0, message); + } + + printf("***Thread id %u exiting\n", threadCtx->ThreadId); + + return hr; +} + +HRESULT +UserScanListenAbortProc ( + _Inout_ PUSER_SCAN_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine is the abort listening thread procedure. + This thread is particularly listening the abortion notifcation from the filter. + The pseudo-code of this function is as follows, + + while(TRUE) { + 1) Wair for and get a message from the filter via FilterGetMessage(...) + 2) Find the scan thread context by its thread id. + 3) Set the cancel flag to be TRUE. + } + +Arguments: + + Context - The user scan context. + +Return Value: + + S_OK if no error occurs; Otherwise, it would return appropriate HRESULT. + +--*/ +{ + HRESULT hr = S_OK; + HANDLE abortPort = NULL; // A port for listening the abort notification from driver. + SCANNER_MESSAGE message; + DWORD dwThisThread = GetCurrentThreadId(); + SCANNER_REPLY_MESSAGE replyMsg; + AV_CONNECTION_CONTEXT connectionCtx = {0}; + PSCANNER_THREAD_CONTEXT threadCtx = NULL; + + ZeroMemory( &message, SCANNER_MESSAGE_SIZE ); + + // + // Prepare the abort communication port. + // + + connectionCtx.Type = AvConnectForAbort; + hr = FilterConnectCommunicationPort( AV_ABORT_PORT_NAME, + 0, + &connectionCtx, + sizeof(AV_CONNECTION_CONTEXT), + NULL, + &abortPort ); + if (FAILED(hr)) { + + abortPort = NULL; + return hr; + } + + // + // This thread is listening an scan abortion notifcation or filter unloading from the kernel + // If it receives notification, its type must be AvMsgAbortScanning or AvMsgFilterUnloading + // + + for(;;) { + + // + // Wait until an abort command is sent from filter. + // + + hr = FilterGetMessage( abortPort, + &message.MessageHeader, + SCANNER_MESSAGE_SIZE, + NULL ); + + if (hr == HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)) { + + printf("[UserScanListenAbortProc]: FilterGetMessage aborted.\n"); + hr = S_OK; + break; + + } else if (FAILED(hr)) { + + fprintf(stderr, + "[UserScanListenAbortProc]: Failed to get message from the minifilter.\n" ); + DisplayError(hr); + continue; + } + + printf("[UserScanListenAbortProc]: Got message %llu. \n", message.MessageHeader.MessageId); + + if (AvMsgAbortScanning == message.Notification.Message) { + + // + // After this thread receives AvMsgAbortScanning + // it does + // 1) Find the user scan thread context + // 2) Set Aborted flag to be TRUE + // + + hr = UserScanGetThreadContextById(message.Notification.ScanThreadId, + Context, + &threadCtx); + if (SUCCEEDED(hr)) { + + printf("[UserScanListenAbortProc]: User Set AvMsgAbortScanning\n"); + + // + // Without critical section here, we cannot prevent the scanner thread from + // proceeding to the next task after we check the Id and before we set the abort flag. + // In short, without critical section, it will be a TOCTTOU bug. + // + EnterCriticalSection(&(threadCtx->Lock)); + if (threadCtx->ScanId == message.Notification.ScanId) { + + threadCtx->Aborted = TRUE; + printf("[UserScanListenAbortProc]: %lld aborted\n", message.Notification.ScanId); + } else { + + printf("[UserScanListenAbortProc]: tried to abort %lld, but current scan in this thread is %lld\n", + message.Notification.ScanId, + threadCtx->ScanId); + } + LeaveCriticalSection(&(threadCtx->Lock)); + + } else { + + fprintf(stderr, "[UserScanListenAbortProc]: Error! UserScanGetThreadContextById failed.\n"); + } + + } else if (AvMsgFilterUnloading == message.Notification.Message) { + + // + // After this thread receives AvMsgFilterUnloading + // it does + // 1) Cancell all the scanning threads + // 2) Wait for them to finish the cancel. + // 3) Reply to filter so that the filter can know it can close the server ports. + // 4) Close scan port, completion port, and abortion port. + // 5) Exit the process + // + + UserScanSynchronizedCancel( Context ); + printf("The filter is unloading, exit!\n"); + ZeroMemory( &replyMsg, SCANNER_REPLY_MESSAGE_SIZE ); + replyMsg.ReplyHeader.MessageId = message.MessageHeader.MessageId; + replyMsg.ThreadId = dwThisThread; + hr = FilterReplyMessage( abortPort, + &replyMsg.ReplyHeader, + SCANNER_REPLY_MESSAGE_SIZE ); + + if (FAILED(hr)) { + + fprintf(stderr, "[UserScanListenAbortProc]: Error! FilterReplyMessage failed.\n"); + } + + UserScanClosePorts( Context ); + CloseHandle( abortPort ); + ExitProcess( 0 ); + break; + + } else { + + assert( FALSE ); // This thread should not receive other kinds of message. + } + + if (FAILED(hr)) { + + fprintf(stderr, "[UserScanListenAbortProc]: Failed to handle the message.\n"); + DisplayError(HRESULT_FROM_WIN32(GetLastError())); + } + } // end of while(TRUE) + + if (!CloseHandle(abortPort)) { + + fprintf(stderr, "[UserScanListenAbortProc]: Failed to close the connection port.\n"); + } + abortPort = NULL; + + return hr; +} + diff --git a/filesys/miniFilter/avscan/user/userscan.h b/filesys/miniFilter/avscan/user/userscan.h new file mode 100644 index 00000000..6666e6d9 --- /dev/null +++ b/filesys/miniFilter/avscan/user/userscan.h @@ -0,0 +1,109 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + userscan.h + +Abstract: + + The scanning module. This module defines the thread contexts, + and user scan contexts, and the definitions of functions. + +Environment: + + User mode + +--*/ + +#ifndef __USERSCAN_H__ +#define __USERSCAN_H__ + +#include <windows.h> +#include <fltUser.h> +#include "avlib.h" + +#ifndef MAKE_HRESULT +#define MAKE_HRESULT(sev,fac,code) \ + ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) +#endif + +typedef struct _SCANNER_THREAD_CONTEXT { + + // + // Threand Handle + // + + HANDLE Handle; + + // + // Threand Id + // + + DWORD ThreadId; + + // + // We need to remember scan id to know which task to abort. + // + + LONGLONG ScanId; + + // + // A flag that indicates that if this scan thread has received cancel callback from the driver + // + + BOOLEAN Aborted; + + // + // A critical section that synchronize the read/write of ScanId and Aborted. + // + + CRITICAL_SECTION Lock; + +} SCANNER_THREAD_CONTEXT, *PSCANNER_THREAD_CONTEXT; + +typedef struct _USER_SCAN_CONTEXT { + + // + // Scan thread contexts + // + + PSCANNER_THREAD_CONTEXT ScanThreadCtxes; + + // + // The abortion thread handle + // + + HANDLE AbortThreadHandle; + + // + // Finalize flag, set at UserScanFinalize(...) + // + + BOOLEAN Finalized; + + // + // Handle of connection port to the filter. + // + + HANDLE ConnectionPort; + + // + // Completion port for asynchronous message passing + // + + HANDLE Completion; + +} USER_SCAN_CONTEXT, *PUSER_SCAN_CONTEXT; + +HRESULT UserScanInit ( + _Inout_ PUSER_SCAN_CONTEXT Context + ); + +HRESULT UserScanFinalize ( + _In_ PUSER_SCAN_CONTEXT Context + ); + +#endif + diff --git a/filesys/miniFilter/avscan/user/utility.c b/filesys/miniFilter/avscan/user/utility.c new file mode 100644 index 00000000..c5321f64 --- /dev/null +++ b/filesys/miniFilter/avscan/user/utility.c @@ -0,0 +1,126 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + utility.c + +Abstract: + + The commonly used routine by the user program. + It will display the HRESULT in a formated message. + +Environment: + + User mode + +--*/ + +#include <windows.h> +#include <Strsafe.h> +#include <stdio.h> +#include "utility.h" + +VOID +DisplayError ( + _In_ DWORD Code + ) + +/*++ + +Routine Description: + + This routine will display an error message based off of the Win32 error + code that is passed in. This allows the user to see an understandable + error message instead of just the code. + +Arguments: + + Code - The error code to be translated. + +Return Value: + + None. + +--*/ + +{ + _Null_terminated_ WCHAR buffer[MAX_PATH] = { 0 }; + DWORD count; + HMODULE module = NULL; + HRESULT status; + + count = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + Code, + 0, + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + + + if (count == 0) { + + count = GetSystemDirectory( buffer, + sizeof(buffer) / sizeof( WCHAR ) ); + + if (count==0 || count > sizeof(buffer) / sizeof( WCHAR )) { + + // + // In practice we expect buffer to be large enough to hold the + // system directory path. + // + + printf(" Could not translate error: %u\n", Code); + return; + } + + + status = StringCchCat( buffer, + sizeof(buffer) / sizeof( WCHAR ), + L"\\fltlib.dll" ); + + if (status != S_OK) { + + printf(" Could not translate error: %u\n", Code); + return; + } + + module = LoadLibraryExW( buffer, NULL, LOAD_LIBRARY_AS_DATAFILE ); + + // + // Translate the Win32 error code into a useful message. + // + + count = FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, + module, + Code, + 0, + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + + if (module != NULL) { + + FreeLibrary( module ); + } + + // + // If we still couldn't resolve the message, generate a string + // + + if (count == 0) { + + printf(" Could not translate error: %u\n", Code); + return; + } + } + + // + // Display the translated error. + // + + printf(" %ws\n", buffer); +} + diff --git a/filesys/miniFilter/avscan/user/utility.h b/filesys/miniFilter/avscan/user/utility.h new file mode 100644 index 00000000..5dcc2dbe --- /dev/null +++ b/filesys/miniFilter/avscan/user/utility.h @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + utility.h + +Abstract: + + The header of commonly used routine by the user program. + +Environment: + + User mode + +--*/ + +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +#include <windows.h> + +VOID +DisplayError ( + _In_ DWORD Code + ); + +#endif + diff --git a/filesys/miniFilter/cancelSafe/ReadMe.md b/filesys/miniFilter/cancelSafe/ReadMe.md new file mode 100644 index 00000000..786b331f --- /dev/null +++ b/filesys/miniFilter/cancelSafe/ReadMe.md @@ -0,0 +1,14 @@ +CancelSafe File System Minifilter Driver +======================================== + +The CancelSafe filter is a sample minifilter that you use if you want to use cancel-safe queues. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *CancelSafe* minifilter initializes a cancel-safe queue when it is attached to a volume. When the minifilter is deployed, it monitors read operations that are passing through the I/O stack. If the read operation is being performed on a file named csqdemo.txt, it is queued onto the cancel-safe queue. Queued operations are completed after a brief pause through a separate worker thread that is running in system context. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.c b/filesys/miniFilter/cancelSafe/cancelSafe.c new file mode 100644 index 00000000..202803ef --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.c @@ -0,0 +1,1932 @@ +/*++ + +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); +} + diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.inf b/filesys/miniFilter/cancelSafe/cancelSafe.inf new file mode 100644 index 00000000..d13586f7 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.inf @@ -0,0 +1,99 @@ +;;; +;;; CancelSafe +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.0 +CatalogFile = cancelsafe.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"OperatingDelay",0x00010001 ,150000000 ; Delay in 100 nano sec units +HKR,,"OperatingPath",0x00000000,%OperatingPath% +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +cancelsafe.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "CancelSafe Mini-Filter Driver" +ServiceName = "CancelSafe" +DriverName = "CancelSafe" +DiskId1 = "CancelSafe Device Installation Disk" +OperatingPath = "\testdir\" + +;Instances specific information. +DefaultInstance = "CancelSafe Instance" +Instance1.Name = "CancelSafe Instance" +Instance1.Altitude = "370050" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.rc b/filesys/miniFilter/cancelSafe/cancelSafe.rc new file mode 100644 index 00000000..39c1f317 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "CancelSafe Filter Driver" +#define VER_INTERNALNAME_STR "cancelSafe.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.sln b/filesys/miniFilter/cancelSafe/cancelSafe.sln new file mode 100644 index 00000000..17d34d65 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancelSafe", "cancelSafe.vcxproj", "{AC99C662-1480-43BF-B6C1-5B3055F228E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|Win32.ActiveCfg = Debug|Win32 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|Win32.Build.0 = Debug|Win32 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|Win32.ActiveCfg = Release|Win32 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|Win32.Build.0 = Release|Win32 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|x64.ActiveCfg = Debug|x64 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Debug|x64.Build.0 = Debug|x64 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|x64.ActiveCfg = Release|x64 + {AC99C662-1480-43BF-B6C1-5B3055F228E2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj new file mode 100644 index 00000000..8e6a1960 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{AC99C662-1480-43BF-B6C1-5B3055F228E2}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{71FE15A0-262E-47F8-BEFF-3C73167F2349}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>cancelSafe</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>cancelSafe</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>cancelSafe</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>cancelSafe</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalOptions>%(AdditionalOptions) /map</AdditionalOptions> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalOptions>%(AdditionalOptions) /map</AdditionalOptions> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalOptions>%(AdditionalOptions) /map</AdditionalOptions> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalOptions>%(AdditionalOptions) /map</AdditionalOptions> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="cancelSafe.c" /> + <ResourceCompile Include="cancelSafe.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters new file mode 100644 index 00000000..11736279 --- /dev/null +++ b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{4671E7BF-8D01-45C4-A3C1-20A08F5BBEDF}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{ABBB6D26-48D1-4341-AC20-F5D5C5329766}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{483287C7-E0B5-452F-BB3A-486026CBBB4F}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{DF4558B9-9BF3-4D98-B89E-718878687D2C}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="cancelSafe.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="cancelSafe.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/cdo/Cdo.rc b/filesys/miniFilter/cdo/Cdo.rc new file mode 100644 index 00000000..4408672c --- /dev/null +++ b/filesys/miniFilter/cdo/Cdo.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Control Device Object Sample Mini-Filter" +#define VER_INTERNALNAME_STR "cdo.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/cdo/CdoInit.c b/filesys/miniFilter/cdo/CdoInit.c new file mode 100644 index 00000000..fa6e37aa --- /dev/null +++ b/filesys/miniFilter/cdo/CdoInit.c @@ -0,0 +1,374 @@ +/*++ + +Copyright (c) 1999 - 2003 Microsoft Corporation + +Module Name: + + CdoInit.c + +Abstract: + + This is the main module of the kernel mode filter driver implementing + the control device object sample. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + + +// +// Local function prototypes +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +CdoUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +CdoInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +#if DBG + +VOID +CdoInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ); + +#endif + + +// +// Global variables +// + +CDO_GLOBAL_DATA Globals; + + +// +// Pragma defintiion table +// + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) + +#if DBG +#pragma alloc_text(INIT, CdoInitializeDebugLevel) +#endif + +#pragma alloc_text(PAGE, CdoUnload) +#endif + + + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS status; + + + // + // This defines what we want to filter with FltMgr + // + + CONST FLT_REGISTRATION filterRegistration = { + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + NULL, // Context + NULL, // Operation callbacks + CdoUnload, // MiniFilterUnload + CdoInstanceSetup, // InstanceSetup + NULL, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + NULL,NULL // NameProvider callbacks + }; + + + RtlZeroMemory( &Globals, sizeof( Globals ) ); + +#if DBG + + // + // Initialize global debug level + // + + CdoInitializeDebugLevel( RegistryPath ); + +#else + + UNREFERENCED_PARAMETER( RegistryPath ); + +#endif + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Cdo]: Driver being loaded\n") ); + + // + // Initialize the resource + // + + ExInitializeResourceLite( &Globals.Resource ); + + // + // Record the driver object + // + + Globals.FilterDriverObject = DriverObject; + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &filterRegistration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + ExDeleteResourceLite( &Globals.Resource ); + return status; + } + + // + // Now create our control device object + // + + status = CdoCreateControlDeviceObject( DriverObject ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( Globals.Filter ); + ExDeleteResourceLite( &Globals.Resource ); + return status; + } + + // + // Start filtering i/o + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + CdoDeleteControlDeviceObject(); + FltUnregisterFilter( Globals.Filter ); + ExDeleteResourceLite( &Globals.Resource ); + return status; + } + + return status; +} + +#if DBG + +VOID +CdoInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the filter DebugLevel parameter from + the registry. This value will be found in the registry location + indicated by the RegistryPath passed in. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + Globals.DebugLevel = DEBUG_TRACE_ERROR; + + // + // Open the desired registry key + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (NT_SUCCESS( status )) { + + // + // Read the DebugFlags value from the registry. + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *((PULONG) &(((PKEY_VALUE_PARTIAL_INFORMATION) buffer)->Data)); + } + } + + // + // Close the registry entry + // + + ZwClose( driverRegKey ); +} + +#endif + + +NTSTATUS +CdoUnload ( + _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. + +--*/ +{ + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Flags ); + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Cdo]: Unloading driver\n") ); + + // + // If the CDO is still referenced and the unload is not mandatry + // then fail the unload + // + + CdoAcquireResourceShared( &Globals.Resource ); + + if (FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF) && + !FlagOn(Flags,FLTFL_FILTER_UNLOAD_MANDATORY)) { + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD | DEBUG_TRACE_ERROR, + ("[Cdo]: Fail unloading driver since the unload is optional and the CDO is open\n") ); + CdoReleaseResource( &Globals.Resource ); + return STATUS_FLT_DO_NOT_DETACH; + } + + + // + // Cleanup and unload + // + + FltUnregisterFilter( Globals.Filter ); + Globals.Filter = NULL; + CdoDeleteControlDeviceObject(); + + + CdoReleaseResource( &Globals.Resource ); + + ExDeleteResourceLite( &Globals.Resource ); + + return STATUS_SUCCESS; +} + + +// +// Instance setup routine +// + +NTSTATUS +CdoInstanceSetup ( + _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. + +Return Value: + + STATUS_FLT_DO_NOT_ATTACH - do not attach because we do not want to + attach to any volume + +--*/ +{ + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + return STATUS_FLT_DO_NOT_ATTACH; +} + + + diff --git a/filesys/miniFilter/cdo/CdoOperations.c b/filesys/miniFilter/cdo/CdoOperations.c new file mode 100644 index 00000000..8193f437 --- /dev/null +++ b/filesys/miniFilter/cdo/CdoOperations.c @@ -0,0 +1,2226 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + operations.c + +Abstract: + + This is the CDO i/o operations module of the kernel mode filter driver implementing + CDO sample + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA + #pragma alloc_text( PAGE, CdoCreateControlDeviceObject) + #pragma alloc_text( PAGE, CdoDeleteControlDeviceObject) + #pragma alloc_text( PAGE, CdoMajorFunction) + #pragma alloc_text( PAGE, CdoHandlePrivateOpen) + #pragma alloc_text( PAGE, CdoHandlePrivateCleanup) + #pragma alloc_text( PAGE, CdoHandlePrivateClose) + #pragma alloc_text( PAGE, CdoHandlePrivateFsControl) + #pragma alloc_text( PAGE, CdoFastIoCheckIfPossible) + #pragma alloc_text( PAGE, CdoFastIoRead) + #pragma alloc_text( PAGE, CdoFastIoWrite) + #pragma alloc_text( PAGE, CdoFastIoQueryBasicInfo) + #pragma alloc_text( PAGE, CdoFastIoQueryStandardInfo) + #pragma alloc_text( PAGE, CdoFastIoLock) + #pragma alloc_text( PAGE, CdoFastIoUnlockSingle) + #pragma alloc_text( PAGE, CdoFastIoUnlockAll) + #pragma alloc_text( PAGE, CdoFastIoUnlockAllByKey) + #pragma alloc_text( PAGE, CdoFastIoDeviceControl) + #pragma alloc_text( PAGE, CdoFastIoQueryNetworkOpenInfo) + #pragma alloc_text( PAGE, CdoFastIoMdlRead) + #pragma alloc_text( NONPAGED, CdoFastIoMdlReadComplete) + #pragma alloc_text( PAGE, CdoFastIoPrepareMdlWrite) + #pragma alloc_text( NONPAGED, CdoFastIoMdlWriteComplete) + #pragma alloc_text( PAGE, CdoFastIoReadCompressed) + #pragma alloc_text( PAGE, CdoFastIoWriteCompressed) + #pragma alloc_text( NONPAGED, CdoFastIoMdlReadCompleteCompressed) + #pragma alloc_text( NONPAGED, CdoFastIoMdlWriteCompleteCompressed) + #pragma alloc_text( PAGE, CdoFastIoQueryOpen) + #pragma alloc_text( PAGE, CdoHandlePrivateOpen ) + #pragma alloc_text( PAGE, CdoHandlePrivateCleanup ) + #pragma alloc_text( PAGE, CdoHandlePrivateClose ) + #pragma alloc_text( PAGE, CdoHandlePrivateFsControl ) + +#endif + + +// +// Fast IO dispatch routines +// + +FAST_IO_DISPATCH CdoFastIoDispatch = +{ + sizeof(FAST_IO_DISPATCH), + CdoFastIoCheckIfPossible, // CheckForFastIo + CdoFastIoRead, // FastIoRead + CdoFastIoWrite, // FastIoWrite + CdoFastIoQueryBasicInfo, // FastIoQueryBasicInfo + CdoFastIoQueryStandardInfo, // FastIoQueryStandardInfo + CdoFastIoLock, // FastIoLock + CdoFastIoUnlockSingle, // FastIoUnlockSingle + CdoFastIoUnlockAll, // FastIoUnlockAll + CdoFastIoUnlockAllByKey, // FastIoUnlockAllByKey + CdoFastIoDeviceControl, // FastIoDeviceControl + NULL, // AcquireFileForNtCreateSection + NULL, // ReleaseFileForNtCreateSection + NULL, // FastIoDetachDevice + CdoFastIoQueryNetworkOpenInfo, // FastIoQueryNetworkOpenInfo + NULL, // AcquireForModWrite + CdoFastIoMdlRead, // MdlRead + CdoFastIoMdlReadComplete, // MdlReadComplete + CdoFastIoPrepareMdlWrite, // PrepareMdlWrite + CdoFastIoMdlWriteComplete, // MdlWriteComplete + CdoFastIoReadCompressed, // FastIoReadCompressed + CdoFastIoWriteCompressed, // FastIoWriteCompressed + CdoFastIoMdlReadCompleteCompressed, // MdlReadCompleteCompressed + CdoFastIoMdlWriteCompleteCompressed, // MdlWriteCompleteCompressed + CdoFastIoQueryOpen, // FastIoQueryOpen + NULL, // ReleaseForModWrite + NULL, // AcquireForCcFlush + NULL, // ReleaseForCcFlush +}; + + + +NTSTATUS +_Function_class_(DRIVER_INITIALIZE) +CdoCreateControlDeviceObject( + _Inout_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine handles the IRPs that are directed to the control + device object. + +Arguments: + + DriverObject - driver object for this driver + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status; + UNICODE_STRING nameString; + ULONG i; + + PAGED_CODE(); + + // + // Create our control device object + // + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE, + ("[Cdo]: Creating CDO ... \n") ); + + RtlInitUnicodeString( &nameString, CONTROL_DEVICE_OBJECT_NAME ); + status = IoCreateDevice( DriverObject, + 0, + &nameString, + FILE_DEVICE_DISK_FILE_SYSTEM, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &Globals.FilterControlDeviceObject); + + if ( !NT_SUCCESS( status ) ) { + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE | DEBUG_TRACE_ERROR, + ("[Cdo]: Failure to create CDO. IoCreateDevice failed with status 0x%x. \n", + status) ); + return status; + } + + // + // Initialize the driver object with this driver's entry points. + // Most are simply passed through to some other device driver. + // + + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { + +#pragma prefast(suppress:__WARNING_DISPATCH_MISMATCH __WARNING_DISPATCH_MISSING, "CdoMajorFunction is the dispatch routine for every major code, so no tag applies to it.") + DriverObject->MajorFunction[i] = CdoMajorFunction; + } + +#pragma prefast(suppress:__WARNING_INACCESSIBLE_MEMBER, "The Cdo sample is allowed to set the FastIo Dispatch routine because he is setting up a Cdo.") + DriverObject->FastIoDispatch = &CdoFastIoDispatch; + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE, + ("[Cdo]: Creating CDO successful\n") ); + + return STATUS_SUCCESS; +} + + + +VOID +CdoDeleteControlDeviceObject( + VOID + ) +/*++ + +Routine Description: + + This routine deletes the control device object. + +Arguments: + + None + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + // + // Delete our control device object + // + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE, + ("[Cdo]: Deleting CDO ... \n") ); + + IoDeleteDevice( Globals.FilterControlDeviceObject ); + + DebugTrace( DEBUG_TRACE_CDO_CREATE_DELETE, + ("[Cdo]: Deleting CDO successful\n") ); + +} + + +DRIVER_DISPATCH CdoMajorFunction; +NTSTATUS +CdoMajorFunction( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles the IRPs that are directed to the control + device object. + +Arguments: + + DeviceObject - control device object + Irp - the current Irp to process + +Return Value: + + Returns STATUS_INVALID_DEVICE_REQUEST if the CDO doesn't support that request + type, or the appropriate status otherwise. + +--*/ +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + + UNREFERENCED_PARAMETER( DeviceObject ); + + PAGED_CODE(); + + FLT_ASSERT( IS_MY_CONTROL_DEVICE_OBJECT( DeviceObject ) ); + + + // + // default to success + // + + status = STATUS_SUCCESS; + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS, + ("[Cdo]: CdoMajorFunction entry ( Irp = %p, irpSp->MajorFunction = 0x%x )\n", + Irp, + irpSp->MajorFunction) ); + + switch (irpSp->MajorFunction) { + + // + // IRP_MJ_CREATE is called to create a new HANDLE on CDO + // + + case IRP_MJ_CREATE: + { + + // + // Handle our private open + // + + status = CdoHandlePrivateOpen(Irp); + + Irp->IoStatus.Status = status; + + if(NT_SUCCESS(status)) + { + // + // If successful, return the file was opened + // + + Irp->IoStatus.Information = FILE_OPENED; + } + else + { + Irp->IoStatus.Information = 0; + } + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + break; + } + + // + // IRP_MJ_CLOSE is called when all references are gone. + // Note: this operation can not be failed. It must succeed. + // + + case IRP_MJ_CLOSE: + { + + CdoHandlePrivateClose( Irp ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + break; + } + + // + // IRP_MJ_DEVICE_CONTROL is how most user-mode api's drop into here + // + + case IRP_MJ_FILE_SYSTEM_CONTROL: + { + ULONG Operation; + ULONG OutputBufferLength; + ULONG InputBufferLength; + PVOID InputBuffer; + PVOID OutputBuffer; + + Operation = irpSp->Parameters.FileSystemControl.FsControlCode; + InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = irpSp->Parameters.FileSystemControl.OutputBufferLength; + + InputBuffer = Irp->AssociatedIrp.SystemBuffer; + OutputBuffer = Irp->AssociatedIrp.SystemBuffer; + + // + // The caller will update the IO status block + // + + status = CdoHandlePrivateFsControl (DeviceObject, + Operation, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength, + &Irp->IoStatus, + Irp ); + break; + } + + // + // IRP_MJ_CLEANUP is called when all handles are closed + // Note: this operation can not be failed. It must succeed. + // + + case IRP_MJ_CLEANUP: + { + + CdoHandlePrivateCleanup( Irp ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + break; + } + + default: + { + // + // unsupported! + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: Unsupported Major Function 0x%x ( Irp = %p )\n", + irpSp->MajorFunction, + Irp) ); + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, IO_NO_INCREMENT ); + + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS, + ("[Cdo]: CdoMajorFunction exit ( Irp = %p, irpSp->MajorFunction = 0x%x, status = 0x%x )\n", + Irp, + irpSp->MajorFunction, + status) ); + + return status; + + +} + + +NTSTATUS +CdoHandlePrivateOpen( + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles create IRPs that are directed to the control + device object. + +Arguments: + + Irp - the current Irp to process + +Return Value: + + Returns STATUS_DEVICE_ALREADY_ATTACHED if the CDO has already been opened + Returns STATUS_SUCCESS otherwise + +Note: + + This sample supports only one outstanding create on the CDO at a time + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( Irp ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateOpen entry ( Irp = %p )\n", + Irp) ); + + CdoAcquireResourceExclusive( &Globals.Resource ); + + if (FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE ) || + FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF )) { + + // + // Sanity - if we have a handle open against this CDO + // we must have an outstanding reference as well + // + + FLT_ASSERT( !FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE ) || + FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ) ); + + + // + // The CDO is already open - fail this open + // + + status = STATUS_DEVICE_ALREADY_ATTACHED; + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateOpen -> Device open failure. Device already opened. ( Irp = %p, Flags = 0x%x, status = 0x%x )\n", + Irp, + Globals.Flags, + status) ); + + } else { + + // + // Flag that the CDO is opened so that we will fail future creates + // until the CDO is closed by the current caller + // + // + // If we suceed the create we are guaranteed to get a Cleanup (where we + // will reset GLOBAL_DATA_F_CDO_OPEN_HANDLE) and Close (where we will + // reset GLOBAL_DATA_F_CDO_OPEN_REF) + // + + SetFlag( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ); + SetFlag( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE ); + + status = STATUS_SUCCESS; + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateOpen -> Device open successful. ( Irp = %p, Flags = 0x%x, status = 0x%x )\n", + Irp, + Globals.Flags, + status) ); + } + + + // + // The filter may want to do additional processing here to set up the structures it + // needs to service this create request. + // + + + CdoReleaseResource( &Globals.Resource ); + + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateOpen exit ( Irp = %p, status = 0x%x )\n", + Irp, + status) ); + + + return status; +} + +NTSTATUS +CdoHandlePrivateCleanup( + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles cleanup IRPs that are directed to the control + device object. + +Arguments: + + Irp - the current Irp to process + +Return Value: + + Returns STATUS_SUCCESS + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( Irp ); + + PAGED_CODE(); + + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateCleanup entry ( Irp = %p )\n", + Irp) ); + + + CdoAcquireResourceExclusive( &Globals.Resource ); + + // + // Sanity - the CDO must have a handle and a reference for us to get a cleanup on it + // + + FLT_ASSERT( FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ) && + FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE) ); + + + // + // Reset the flag that indicates the CDO has a open handle + // + + ClearFlag( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE); + + status = STATUS_SUCCESS; + + // + // The filter may want to do additional processing here to cleanup up the structures it + // needed to service the handle that is being closed. + // + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateCleanup -> Device cleanup successful. ( Irp = %p, Flags = 0x%x, status = 0x%x )\n", + Irp, + Globals.Flags, + status) ); + + + CdoReleaseResource( &Globals.Resource ); + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateCleanup exit ( Irp = %p, status = 0x%x )\n", + Irp, + status) ); + + + + return status; +} + +NTSTATUS +CdoHandlePrivateClose( + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine handles close IRPs that are directed to the control + device object. + +Arguments: + + Irp - the current Irp to process + +Return Value: + + Returns STATUS_SUCCESS + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( Irp ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateClose entry ( Irp = %p )\n", + Irp) ); + + CdoAcquireResourceExclusive( &Globals.Resource ); + + // + // Sanity - the connection must have a reference but have no handle open, + // for us to get a close on it + // + + FLT_ASSERT( FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ) && + !FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE )); + + + // + // Reset the flag that indicates the CDO is opened so that we will suceed + // future creates + // + + ClearFlag( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ); + + + // + // The filter may want to do additional processing here to cleanup up the structures it + // needed to service the user mode attachment that is being closed. + // + + + status = STATUS_SUCCESS; + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateClose -> Device close successful. ( Irp = %p, Flags = 0x%x, status = 0x%x )\n", + Irp, + Globals.Flags, + status) ); + + CdoReleaseResource( &Globals.Resource ); + + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateClose exit ( Irp = %p, status = 0x%x )\n", + Irp, + status) ); + + + return status; + +} + +NTSTATUS +CdoHandlePrivateFsControl ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ ULONG IoControlCode, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_opt_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine is invoked whenever an I/O Request Packet (IRP) w/a major + function code of IRP_MJ_FILE_SYSTEM_CONTROL is encountered for the CDO. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + IoControlCode - Control code for this IOCTL + InputBuffer - Input buffer + InputBufferLength - Input buffer length + OutputBuffer - Output buffer + OutputBufferLength - Output buffer length + IoStatus - IO status block for this request + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( IoControlCode ); + UNREFERENCED_PARAMETER( InputBuffer ); + UNREFERENCED_PARAMETER( InputBufferLength ); + UNREFERENCED_PARAMETER( OutputBuffer ); + UNREFERENCED_PARAMETER( OutputBufferLength ); + UNREFERENCED_PARAMETER( Irp ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateFsControl entry ( Irp = %p )\n" + "\tIoControlCode = 0x%x\n" + "\tInputBuffer = %p\n" + "\tInputBufferLength = 0x%x\n" + "\tOutputBuffer = %p\n" + "\tOutputBufferLength = 0x%x\n", + Irp, + IoControlCode, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength) ); + + CdoAcquireResourceShared( &Globals.Resource ); + + // + // Sanity - there must atleast be a reference open for us to get a IOCTL on the CDO + // + + FLT_ASSERT( FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_REF ) ); + + + if (!FlagOn( Globals.Flags, GLOBAL_DATA_F_CDO_OPEN_HANDLE)) { + + // + // If there is no handle open to the CDO fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoHandlePrivateFsControl -> Failing IOCTL since no handle to CDO is open. ( Irp = %p, IoControlCode = 0x%x, Flags = 0x%x )\n", + Irp, + IoControlCode, + Globals.Flags) ); + + status = STATUS_INVALID_DEVICE_STATE; + CdoReleaseResource( &Globals.Resource ); + goto CdoHandlePrivateFsControlCleanup; + } + + // + // Here the filter may perform any action that requires that + // the handle to the CDO still be open + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateFsControl -> Processing IOCTL while handle to CDO is definitely open. ( Irp = %p, IoControlCode = 0x%x )\n", + Irp, + IoControlCode) ); + + CdoReleaseResource( &Globals.Resource ); + + // + // Since the resource has been released the CDO may complete a cleanup before we + // do any of the following. + // + + + // + // Here the filter may perform any action that does not require that + // the handle to the CDO still be open. For example, the IOCTL may have + // been used to trigger off an asynchronous background task that will + // continue executing even after the handle has been closed + // + // Note that the system will still maintain a reference to the CDO. So, + // the filter will not see a Close on the CDO until it finishes servicing + // IRP_MJ_FILE_SYSTEM_CONTROL + // + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateFsControl -> Processing IOCTL while handle to CDO may not be open. ( Irp = %p, IoControlCode = 0x%x )\n", + Irp, + IoControlCode) ); + + status = STATUS_SUCCESS; + +CdoHandlePrivateFsControlCleanup: + + IoStatus->Status = status; + IoStatus->Information = 0; + + DebugTrace( DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoHandlePrivateFsControl exit ( Irp = %p, IoControlCode = 0x%x, status = 0x%x )\n", + Irp, + IoControlCode, + status) ); + + + return status; +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// FastIO Handling routines +// +///////////////////////////////////////////////////////////////////////////// + + + +BOOLEAN +CdoFastIoCheckIfPossible ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_ BOOLEAN CheckForReadOperation, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for checking to see + whether fast I/O is possible for this file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be operated on. + + FileOffset - Byte offset in the file for the operation. + + Length - Length of the operation to be performed. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + LockKey - Provides the caller's key for file locks. + + CheckForReadOperation - Indicates whether the caller is checking for a + read (TRUE) or a write operation. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(CheckForReadOperation); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoCheckIfPossible -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +BOOLEAN +CdoFastIoRead ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _Out_writes_bytes_(Length) PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for reading from a + file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be read. + + FileOffset - Byte offset in the file of the read. + + Length - Length of the read operation to be performed. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + LockKey - Provides the caller's key for file locks. + + Buffer - Pointer to the caller's buffer to receive the data read. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoRead -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + +BOOLEAN +CdoFastIoWrite ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_reads_bytes_(Length) PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for writing to a + file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be written. + + FileOffset - Byte offset in the file of the write operation. + + Length - Length of the write operation to be performed. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + LockKey - Provides the caller's key for file locks. + + Buffer - Pointer to the caller's buffer that contains the data to be + written. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoWrite -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryBasicInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_BASIC_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for querying basic + information about the file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be queried. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + Buffer - Pointer to the caller's buffer to receive the information about + the file. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoQueryBasicInfo -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryStandardInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_STANDARD_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for querying standard + information about the file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be queried. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + Buffer - Pointer to the caller's buffer to receive the information about + the file. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoQueryStandardInfo -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +BOOLEAN +CdoFastIoLock ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _In_ BOOLEAN FailImmediately, + _In_ BOOLEAN ExclusiveLock, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for locking a byte + range within a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be locked. + + FileOffset - Starting byte offset from the base of the file to be locked. + + Length - Length of the byte range to be locked. + + ProcessId - ID of the process requesting the file lock. + + Key - Lock key to associate with the file lock. + + FailImmediately - Indicates whether or not the lock request is to fail + if it cannot be immediately be granted. + + ExclusiveLock - Indicates whether the lock to be taken is exclusive (TRUE) + or shared. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(ProcessId); + UNREFERENCED_PARAMETER(Key); + UNREFERENCED_PARAMETER(FailImmediately); + UNREFERENCED_PARAMETER(ExclusiveLock); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoLock -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +BOOLEAN +CdoFastIoUnlockSingle ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for unlocking a byte + range within a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be unlocked. + + FileOffset - Starting byte offset from the base of the file to be + unlocked. + + Length - Length of the byte range to be unlocked. + + ProcessId - ID of the process requesting the unlock operation. + + Key - Lock key associated with the file lock. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(ProcessId); + UNREFERENCED_PARAMETER(Key); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoUnlockSingle -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + +BOOLEAN +CdoFastIoUnlockAll ( + _In_ PFILE_OBJECT FileObject, + _In_ PEPROCESS ProcessId, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for unlocking all + locks within a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be unlocked. + + ProcessId - ID of the process requesting the unlock operation. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(ProcessId); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoUnlockAll -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +BOOLEAN +CdoFastIoUnlockAllByKey ( + _In_ PFILE_OBJECT FileObject, + _In_ PVOID ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for unlocking all + locks within a file based on a specified key. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be unlocked. + + ProcessId - ID of the process requesting the unlock operation. + + Key - Lock key associated with the locks on the file to be released. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(ProcessId); + UNREFERENCED_PARAMETER(Key); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoUnlockAllByKey -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + +BOOLEAN +CdoFastIoDeviceControl ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ ULONG IoControlCode, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for device I/O control + operations on a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object representing the device to be + serviced. + + Wait - Indicates whether or not the caller is willing to wait if the + appropriate locks, etc. cannot be acquired + + InputBuffer - Optional pointer to a buffer to be passed into the driver. + + InputBufferLength - Length of the optional InputBuffer, if one was + specified. + + OutputBuffer - Optional pointer to a buffer to receive data from the + driver. + + OutputBufferLength - Length of the optional OutputBuffer, if one was + specified. + + IoControlCode - I/O control code indicating the operation to be performed + on the device. + + IoStatus - Pointer to a variable to receive the I/O status of the + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Wait); + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoFastIoDeviceControl Entry ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + // + // The caller will update the IO status block + // + + CdoHandlePrivateFsControl ( DeviceObject, + IoControlCode, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength, + IoStatus, + NULL ); + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS, + ("[Cdo]: CdoFastIoDeviceControl Exit ( FileObject = %p, DeviceObject = %p, Status = 0x%x )\n", + FileObject, + DeviceObject, + IoStatus->Status) ); + + return TRUE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryNetworkOpenInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_NETWORK_OPEN_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for querying network + information about a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object to be queried. + + Wait - Indicates whether or not the caller can handle the file system + having to wait and tie up the current thread. + + Buffer - Pointer to a buffer to receive the network information about the + file. + + IoStatus - Pointer to a variable to receive the final status of the query + operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(Wait); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoQueryNetworkOpenInfo -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoMdlRead ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for reading a file + using MDLs as buffers. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object that is to be read. + + FileOffset - Supplies the offset into the file to begin the read operation. + + Length - Specifies the number of bytes to be read from the file. + + LockKey - The key to be used in byte range lock checks. + + MdlChain - A pointer to a variable to be filled in w/a pointer to the MDL + chain built to describe the data read. + + IoStatus - Variable to receive the final status of the read operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlRead -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + +BOOLEAN +CdoFastIoMdlReadComplete ( + _In_ PFILE_OBJECT FileObject, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for completing an + MDL read operation. + + This function simply invokes the file system's corresponding routine, if + it has one. It should be the case that this routine is invoked only if + the MdlRead function is supported by the underlying file system, and + therefore this function will also be supported, but this is not assumed + by this driver. + +Arguments: + + FileObject - Pointer to the file object to complete the MDL read upon. + + MdlChain - Pointer to the MDL chain used to perform the read operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE, depending on whether or not it is + possible to invoke this function on the fast I/O path. + +--*/ + +{ + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, return not supported + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlReadComplete -> Unsupported as FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + return FALSE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoPrepareMdlWrite ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for preparing for an + MDL write operation. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object that will be written. + + FileOffset - Supplies the offset into the file to begin the write operation. + + Length - Specifies the number of bytes to be write to the file. + + LockKey - The key to be used in byte range lock checks. + + MdlChain - A pointer to a variable to be filled in w/a pointer to the MDL + chain built to describe the data written. + + IoStatus - Variable to receive the final status of the write operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoPrepareMdlWrite -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + + +BOOLEAN +CdoFastIoMdlWriteComplete ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject ) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for completing an + MDL write operation. + + This function simply invokes the file system's corresponding routine, if + it has one. It should be the case that this routine is invoked only if + the PrepareMdlWrite function is supported by the underlying file system, + and therefore this function will also be supported, but this is not + assumed by this driver. + +Arguments: + + FileObject - Pointer to the file object to complete the MDL write upon. + + FileOffset - Supplies the file offset at which the write took place. + + MdlChain - Pointer to the MDL chain used to perform the write operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE, depending on whether or not it is + possible to invoke this function on the fast I/O path. + +--*/ +{ + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, return not supported + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlWriteComplete -> Unsupported as FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + + return FALSE; +} + + +/********************************************************************************* + UNIMPLEMENTED FAST IO ROUTINES + + The following four Fast IO routines are for compression on the wire + which is not yet implemented in NT. + + NOTE: It is highly recommended that you include these routines (which + do a pass-through call) so your filter will not need to be + modified in the future when this functionality is implemented in + the OS. + + FastIoReadCompressed, FastIoWriteCompressed, + FastIoMdlReadCompleteCompressed, FastIoMdlWriteCompleteCompressed +**********************************************************************************/ + + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoReadCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Out_writes_bytes_(Length) PVOID Buffer, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _Out_writes_bytes_(CompressedDataInfoLength) struct _COMPRESSED_DATA_INFO *CompressedDataInfo, + _In_ ULONG CompressedDataInfoLength, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for reading compressed + data from a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object that will be read. + + FileOffset - Supplies the offset into the file to begin the read operation. + + Length - Specifies the number of bytes to be read from the file. + + LockKey - The key to be used in byte range lock checks. + + Buffer - Pointer to a buffer to receive the compressed data read. + + MdlChain - A pointer to a variable to be filled in w/a pointer to the MDL + chain built to describe the data read. + + IoStatus - Variable to receive the final status of the read operation. + + CompressedDataInfo - A buffer to receive the description of the compressed + data. + + CompressedDataInfoLength - Specifies the size of the buffer described by + the CompressedDataInfo parameter. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(CompressedDataInfo); + UNREFERENCED_PARAMETER(CompressedDataInfoLength); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoReadCompressed -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoWriteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _In_reads_bytes_(Length) PVOID Buffer, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_reads_bytes_(CompressedDataInfoLength) struct _COMPRESSED_DATA_INFO *CompressedDataInfo, + _In_ ULONG CompressedDataInfoLength, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for writing compressed + data to a file. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + FileObject - Pointer to the file object that will be written. + + FileOffset - Supplies the offset into the file to begin the write operation. + + Length - Specifies the number of bytes to be write to the file. + + LockKey - The key to be used in byte range lock checks. + + Buffer - Pointer to the buffer containing the data to be written. + + MdlChain - A pointer to a variable to be filled in w/a pointer to the MDL + chain built to describe the data written. + + IoStatus - Variable to receive the final status of the write operation. + + CompressedDataInfo - A buffer to containing the description of the + compressed data. + + CompressedDataInfoLength - Specifies the size of the buffer described by + the CompressedDataInfo parameter. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ + +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); + UNREFERENCED_PARAMETER(LockKey); + UNREFERENCED_PARAMETER(Buffer); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(CompressedDataInfo); + UNREFERENCED_PARAMETER(CompressedDataInfoLength); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoWriteCompressed -> Unsupported FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST; + IoStatus->Information = 0; + + return TRUE; +} + + + + +BOOLEAN +CdoFastIoMdlReadCompleteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for completing an + MDL read compressed operation. + + This function simply invokes the file system's corresponding routine, if + it has one. It should be the case that this routine is invoked only if + the read compressed function is supported by the underlying file system, + and therefore this function will also be supported, but this is not assumed + by this driver. + +Arguments: + + FileObject - Pointer to the file object to complete the compressed read + upon. + + MdlChain - Pointer to the MDL chain used to perform the read operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE, depending on whether or not it is + possible to invoke this function on the fast I/O path. + +--*/ +{ + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, return not supported + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlReadCompleteCompressed -> Unsupported as FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + return FALSE; +} + + + +BOOLEAN +CdoFastIoMdlWriteCompleteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for completing a + write compressed operation. + + This function simply invokes the file system's corresponding routine, if + it has one. It should be the case that this routine is invoked only if + the write compressed function is supported by the underlying file system, + and therefore this function will also be supported, but this is not assumed + by this driver. + +Arguments: + + FileObject - Pointer to the file object to complete the compressed write + upon. + + FileOffset - Supplies the file offset at which the file write operation + began. + + MdlChain - Pointer to the MDL chain used to perform the write operation. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE, depending on whether or not it is + possible to invoke this function on the fast I/O path. + +--*/ +{ + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(FileObject); + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(MdlChain); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, return not supported + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoMdlWriteCompleteCompressed -> Unsupported as FastIO call ( FileObject = %p, DeviceObject = %p )\n", + FileObject, + DeviceObject) ); + + return FALSE; +} + + +// This annotation tells the static analyzer that IoStatus->Status is where to check +// whether this routine succeeded or not, not the BOOLEAN return value. +_Success_(Irp->IoStatus.Status == 0) +BOOLEAN +CdoFastIoQueryOpen ( + _In_ PIRP Irp, + _Out_ PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, + _In_ PDEVICE_OBJECT DeviceObject) +/*++ + +Routine Description: + + This routine is the fast I/O "pass through" routine for opening a file + and returning network information for it. + + This function simply invokes the file system's corresponding routine, or + returns FALSE if the file system does not implement the function. + +Arguments: + + Irp - Pointer to a create IRP that represents this open operation. It is + to be used by the file system for common open/create code, but not + actually completed. + + NetworkInformation - A buffer to receive the information required by the + network about the file being opened. + + DeviceObject - Pointer to this driver's device object, the device on + which the operation is to occur. + +Return Value: + + The function value is TRUE or FALSE based on whether or not fast I/O + is possible for this file. + +--*/ +{ + PAGED_CODE(); + FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)); + + UNREFERENCED_PARAMETER(NetworkInformation); + UNREFERENCED_PARAMETER(DeviceObject); + + // + // This is our CDO, fail the operation + // + + DebugTrace( DEBUG_TRACE_CDO_ALL_OPERATIONS | DEBUG_TRACE_CDO_FASTIO_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Cdo]: CdoFastIoQueryOpen -> Unsupported FastIO call ( Irp = %p, DeviceObject = %p )\n", + Irp, + DeviceObject) ); + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + + return TRUE; +} + + + diff --git a/filesys/miniFilter/cdo/CdoProc.h b/filesys/miniFilter/cdo/CdoProc.h new file mode 100644 index 00000000..4efe27ad --- /dev/null +++ b/filesys/miniFilter/cdo/CdoProc.h @@ -0,0 +1,328 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + CdoProc.h + +Abstract: + + This is the header file defining the functions of the kernel mode + filter driver implementing the CDO sample. + + +Environment: + + Kernel mode + + +--*/ + + +// +// Functions implemented in operations.c +// + + + +// +// Functions implemented in CdoOperations.c +// + +_Function_class_(DRIVER_INITIALIZE) +NTSTATUS +CdoCreateControlDeviceObject( + _Inout_ PDRIVER_OBJECT DriverObject + ); + +VOID +CdoDeleteControlDeviceObject( + VOID + ); + +// +// Functions implemented in CdoOperations.c +// + +DRIVER_DISPATCH CdoMajorFunction; +NTSTATUS +CdoMajorFunction( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ); + +NTSTATUS +CdoHandlePrivateOpen( + _In_ PIRP Irp + ); + +NTSTATUS +CdoHandlePrivateCleanup( + _In_ PIRP Irp + ); + +NTSTATUS +CdoHandlePrivateClose( + _In_ PIRP Irp + ); + +NTSTATUS +CdoHandlePrivateFsControl ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ ULONG IoControlCode, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_opt_ PIRP Irp + ); + +BOOLEAN +CdoFastIoCheckIfPossible ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_ BOOLEAN CheckForReadOperation, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoRead ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _Out_writes_bytes_(Length) PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoWrite ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ BOOLEAN Wait, + _In_ ULONG LockKey, + _In_reads_bytes_(Length) PVOID Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryBasicInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_BASIC_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryStandardInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_STANDARD_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoLock ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _In_ BOOLEAN FailImmediately, + _In_ BOOLEAN ExclusiveLock, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoUnlockSingle ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PLARGE_INTEGER Length, + _In_ PEPROCESS ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoUnlockAll ( + _In_ PFILE_OBJECT FileObject, + _In_ PEPROCESS ProcessId, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoUnlockAllByKey ( + _In_ PFILE_OBJECT FileObject, + _In_ PVOID ProcessId, + _In_ ULONG Key, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoDeviceControl ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ ULONG IoControlCode, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoQueryNetworkOpenInfo ( + _In_ PFILE_OBJECT FileObject, + _In_ BOOLEAN Wait, + _Out_ PFILE_NETWORK_OPEN_INFORMATION Buffer, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoMdlRead ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoMdlReadComplete ( + _In_ PFILE_OBJECT FileObject, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoPrepareMdlWrite ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_ PDEVICE_OBJECT DeviceObject ); + +BOOLEAN +CdoFastIoMdlWriteComplete ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject ); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoReadCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _Out_writes_bytes_(Length) PVOID Buffer, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _Out_writes_bytes_(CompressedDataInfoLength) struct _COMPRESSED_DATA_INFO *CompressedDataInfo, + _In_ ULONG CompressedDataInfoLength, + _In_ PDEVICE_OBJECT DeviceObject); + +_Success_(IoStatus->Status == 0) +BOOLEAN +CdoFastIoWriteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ ULONG Length, + _In_ ULONG LockKey, + _In_reads_bytes_(Length) PVOID Buffer, + _Outptr_ PMDL *MdlChain, + _Out_ PIO_STATUS_BLOCK IoStatus, + _In_reads_bytes_(CompressedDataInfoLength) struct _COMPRESSED_DATA_INFO *CompressedDataInfo, + _In_ ULONG CompressedDataInfoLength, + _In_ PDEVICE_OBJECT DeviceObject); + +BOOLEAN +CdoFastIoMdlReadCompleteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject); + +BOOLEAN +CdoFastIoMdlWriteCompleteCompressed ( + _In_ PFILE_OBJECT FileObject, + _In_ PLARGE_INTEGER FileOffset, + _In_ PMDL MdlChain, + _In_ PDEVICE_OBJECT DeviceObject); + +_Success_(Irp->IoStatus.Status == 0) +BOOLEAN +CdoFastIoQueryOpen ( + _In_ PIRP Irp, + _Out_ PFILE_NETWORK_OPEN_INFORMATION NetworkInformation, + _In_ PDEVICE_OBJECT DeviceObject); + + + + +// +// Resource support +// + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +CdoAcquireResourceExclusive ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_exclusive_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + !ExIsResourceAcquiredSharedLite(Resource)); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +CdoAcquireResourceShared ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_shared_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceSharedLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(_Global_critical_region_) +CdoReleaseResource ( + _Inout_ _Requires_lock_held_(*_Curr_) _Releases_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + ExIsResourceAcquiredSharedLite(Resource)); + + ExReleaseResourceLite(Resource); + KeLeaveCriticalRegion(); +} + + diff --git a/filesys/miniFilter/cdo/CdoStruct.h b/filesys/miniFilter/cdo/CdoStruct.h new file mode 100644 index 00000000..6e05a9b1 --- /dev/null +++ b/filesys/miniFilter/cdo/CdoStruct.h @@ -0,0 +1,137 @@ +/*++ + +Copyright (c) 1999 - 2003 Microsoft Corporation + +Module Name: + + CdoStruct.h + +Abstract: + + This is the header file defining the data structures used by the kernel mode + filter driver implementing the control device object sample. + + +Environment: + + Kernel mode + + +--*/ + +// +// CDO sample filter global data +// + +// +// GLOBAL_DATA_F_xxx flags +// + +// +// Indicates that there is a open reference to the CDO +// +#define GLOBAL_DATA_F_CDO_OPEN_REF 0x00000001 + +// +// Indicates that there is a open handle to the CDO +// + +#define GLOBAL_DATA_F_CDO_OPEN_HANDLE 0x00000002 + +// +// Globals +// + +typedef struct _CDO_GLOBAL_DATA { + + // + // Handle to minifilter returned from FltRegisterFilter() + // + + PFLT_FILTER Filter; + + // + // Driver object for this filter + // + + PDRIVER_OBJECT FilterDriverObject; + + // + // Control Device Object for this filter + // + + PDEVICE_OBJECT FilterControlDeviceObject; + + // + // Flags - GLOBAL_DATA_F_xxx + // + + ULONG Flags; + + // + // Resource to synchronize access to flags + // + + ERESOURCE Resource; + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + +} CDO_GLOBAL_DATA, *PCDO_GLOBAL_DATA; + +extern CDO_GLOBAL_DATA Globals; + +// +// The name of the CDO created by this filter +// + +#define CONTROL_DEVICE_OBJECT_NAME L"\\FileSystem\\Filters\\CdoSample" + +// +// Macro to test if this is my control device object +// + +#define IS_MY_CONTROL_DEVICE_OBJECT(_devObj) \ + (((_devObj) == Globals.FilterControlDeviceObject) ? \ + (FLT_ASSERT(((_devObj)->DriverObject == Globals.FilterDriverObject) && \ + ((_devObj)->DeviceExtension == NULL)), TRUE) : \ + FALSE) + + +// +// 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_CDO_CREATE_DELETE 0x00000004 // Creation/Deletion of CDO +#define DEBUG_TRACE_CDO_SUPPORTED_OPERATIONS 0x00000008 // Supported operations on CDO +#define DEBUG_TRACE_CDO_FASTIO_OPERATIONS 0x00000010 // FastIO operations on CDO +#define DEBUG_TRACE_CDO_ALL_OPERATIONS 0x00000020 // All operations on CDO + +#define DEBUG_TRACE_ALL 0xFFFFFFFF // All flags + + +#define DebugTrace(Level, Data) \ + if ((Level) & Globals.DebugLevel) { \ + DbgPrint Data; \ + } + + +#else + +#define DebugTrace(Level, Data) {NOTHING;} + +#endif + + diff --git a/filesys/miniFilter/cdo/ReadMe.md b/filesys/miniFilter/cdo/ReadMe.md new file mode 100644 index 00000000..750de7b7 --- /dev/null +++ b/filesys/miniFilter/cdo/ReadMe.md @@ -0,0 +1,16 @@ +CDO File System Minifilter Driver +================================= + +The CDO minifilter sample is an example if you intend to use a control device object (CDO) with your minifilters. + +Although the filter manager infrastructure provides a message interface for communication between applications and minifilters, you might need explicit CDOs while the minifilters interface with legacy software. This sample shows how to create and use a CDO with minifilters. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +When the CDO minifilter is deployed, it creates a CDO object named "FileSystem\\Filters\\CdoSample" in the Microsoft Windows object namespace and enables applications to open it and perform certain operations on it. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. diff --git a/filesys/miniFilter/cdo/cdo.inf b/filesys/miniFilter/cdo/cdo.inf new file mode 100644 index 00000000..327fba11 --- /dev/null +++ b/filesys/miniFilter/cdo/cdo.inf @@ -0,0 +1,96 @@ +;;; +;;; Control Device Object File System Filter Driver Sample +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = cdo.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugLevel",0x00010001,0x00000001 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +cdo.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Control Device Object File System Filter Driver Sample" +ServiceName = "CDO" +DriverName = "cdo" +DiskId1 = "CDO Device Installation Disk" + +;Instances specific information. +DefaultInstance = "CDO" +Instance1.Name = "CDO" +Instance1.Altitude = "370080" +Instance1.Flags = 0x0 diff --git a/filesys/miniFilter/cdo/cdo.sln b/filesys/miniFilter/cdo/cdo.sln new file mode 100644 index 00000000..c0fd8f6d --- /dev/null +++ b/filesys/miniFilter/cdo/cdo.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdo", "cdo.vcxproj", "{1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|Win32.Build.0 = Debug|Win32 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|Win32.ActiveCfg = Release|Win32 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|Win32.Build.0 = Release|Win32 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|x64.ActiveCfg = Debug|x64 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Debug|x64.Build.0 = Debug|x64 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|x64.ActiveCfg = Release|x64 + {1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/cdo/cdo.vcxproj b/filesys/miniFilter/cdo/cdo.vcxproj new file mode 100644 index 00000000..52dc8e52 --- /dev/null +++ b/filesys/miniFilter/cdo/cdo.vcxproj @@ -0,0 +1,153 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{1C6DC452-62DC-48E5-9C9F-B32BDF4C8F2F}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{6ABBD5BA-9307-4ABF-821A-66D082F38769}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>cdo</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>cdo</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>cdo</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>cdo</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="CdoInit.c" /> + <ClCompile Include="CdoOperations.c" /> + <ResourceCompile Include="cdo.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/cdo/cdo.vcxproj.Filters b/filesys/miniFilter/cdo/cdo.vcxproj.Filters new file mode 100644 index 00000000..04f6e7f0 --- /dev/null +++ b/filesys/miniFilter/cdo/cdo.vcxproj.Filters @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{33D6FDE5-707F-4D9E-A316-A031D1F83BC8}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{3BD87E43-9C30-4470-9754-3114B37E0BFD}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{4A9AC38F-BAFE-45E1-929D-047A9F3680ED}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{AA17AAA7-BDC6-41C8-9342-FF2F9D6AE3CB}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="CdoInit.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CdoOperations.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="cdo.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/cdo/pch.h b/filesys/miniFilter/cdo/pch.h new file mode 100644 index 00000000..c932a141 --- /dev/null +++ b/filesys/miniFilter/cdo/pch.h @@ -0,0 +1,48 @@ +/*++ + +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 __CDO_PCH_H__ +#define __CDO_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 <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> +#include "CdoStruct.h" +#include "CdoProc.h" + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +#endif __CDO_PCH_H__ + diff --git a/filesys/miniFilter/change/ReadMe.md b/filesys/miniFilter/change/ReadMe.md new file mode 100644 index 00000000..b40130e6 --- /dev/null +++ b/filesys/miniFilter/change/ReadMe.md @@ -0,0 +1,17 @@ +Change File System Minifilter Driver +==================================== + +The Change minifilter is a transaction-aware filter that monitors file changes in real time. + +This filter tracks if the files are 'dirty' by intercepting write I/O requests. This provides a way to track modifications to a file. Additionally, this filter handles the case where the transaction commits or rollbacks. + +The primary tasks of the filter for tracking a transacted file are the following: + +1. In the post create callback, if a transacted file is open with attribute FILE\_WRITE\_DATA or FILE\_APPEND\_DATA, then enlist its file context into the transaction context. +2. In the pre-operation callback, if the operation needs to be dirty, such as IRP\_MJ\_WRITE and the file is part of a transaction, update the transacted dirty record instead of the non-transacted dirty record. +3. In the kernel transaction manager (KTM) notification callback, if the transaction is committed, then propagate the dirty information from the transacted dirty record to the non-transacted dirty record; if rollback, do not propagate. +4. Properly remove the context structure in the TransactionContextCleanup routine. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + diff --git a/filesys/miniFilter/change/change.c b/filesys/miniFilter/change/change.c new file mode 100644 index 00000000..ec9096c0 --- /dev/null +++ b/filesys/miniFilter/change/change.c @@ -0,0 +1,1393 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + change.c + +Abstract: + + This is the main module of the change miniFilter driver. + This transaction-aware filter monitors file changes in real time. + Cg prefix denotes "Change" module. + + This module tracks if the files are dirty. In order to do this, + we have to intercept the "write" I/O requests. In particular, + the operations are collected in CgOperationsNeedDirty(...) function. + If you care about more than the contents of the file, you may need to + modify this function accordingly. + + This sample demonstrates how to track whether a file has been modified. + + In addition, this filter handles the case that the transaction + commits or rollbacks. The overview of a transaction-aware + minifilter is stated as follows + + 1. At post create, if a transacted file is open with attribute + FILE_WRITE_DATA or FILE_APPEND_DATA, then we would enlist its + file context into the transaction context. + + 2. At pre-operation callback, if the operation needs to be dirty, + such as IRP_MJ_WRITE and the file is part of a transaction, + we update its TxDirty instead of Dirty. + + 3. At KTM notification callback, if the transaction committed, + then propagate the dirty information from TxDirty to Dirty; + if rollbacked, do not propagate. + + 4. Properly remove the list at TransactionContextCleanup. + +Environment: + + Kernel mode + +--*/ + +#include "change.h" + +/************************************************************************* + Local Function Prototypes +*************************************************************************/ + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +CgInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +CgInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +CgInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +CgUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +CgInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +CgPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +CgPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +CgPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +CgPreClose ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +CgPreFsControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +NTSTATUS +CgKtmNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ); + +// +// Local routines +// + +BOOLEAN +CgOperationsNeedDirty ( + _In_ PFLT_CALLBACK_DATA Data + ); + +NTSTATUS +CgQueryTransactionOutcome( + _In_ PKTRANSACTION Transaction, + _Out_ PULONG TxOutcome + ); + +NTSTATUS +CgProcessPreviousTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PCG_FILE_CONTEXT FileContext + ); + +NTSTATUS +CgProcessTransactionOutcome( + _Inout_ PCG_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG TransactionOutcome + ); + + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, CgUnload) +#pragma alloc_text(PAGE, CgInstanceQueryTeardown) +#pragma alloc_text(PAGE, CgInstanceSetup) +#pragma alloc_text(PAGE, CgInstanceTeardownStart) +#pragma alloc_text(PAGE, CgInstanceTeardownComplete) +#pragma alloc_text(PAGE, CgInstanceTeardownComplete) +#pragma alloc_text(PAGE, CgPreCreate) +#pragma alloc_text(PAGE, CgPreFsControl) +#pragma alloc_text(PAGE, CgPostCreate) +#pragma alloc_text(PAGE, CgPreClose) +#pragma alloc_text(PAGE, CgKtmNotificationCallback) +#pragma alloc_text(PAGE, CgProcessPreviousTransaction) +#pragma alloc_text(PAGE, CgProcessTransactionOutcome) +#pragma alloc_text(PAGE, CgQueryTransactionOutcome) +#endif + + +// +// operation registration +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + CgPreCreate, + CgPostCreate }, + + { IRP_MJ_CLOSE, + 0, + CgPreClose, + NULL }, + + { IRP_MJ_WRITE, + 0, + CgPreOperationCallback, + NULL }, + + { IRP_MJ_SET_INFORMATION, + 0, + CgPreOperationCallback, + NULL }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + CgPreFsControl, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Context registraction construct defined in context.c +// + +extern const FLT_CONTEXT_REGISTRATION ContextRegistration[]; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + ContextRegistration, // Context + Callbacks, // Operation callbacks + + CgUnload, // MiniFilterUnload + + CgInstanceSetup, // InstanceSetup + CgInstanceQueryTeardown, // InstanceQueryTeardown + CgInstanceTeardownStart, // InstanceTeardownStart + CgInstanceTeardownComplete, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL, // NormalizeNameComponent + CgKtmNotificationCallback // KTM notification callback + +}; + + + +NTSTATUS +CgInstanceSetup ( + _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. + + If this routine is not defined in the registration structure, automatic + instances are always created. + +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. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgInstanceSetup: Entered\n") ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +CgInstanceQueryTeardown ( + _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. + + If this routine is not defined in the registration structure, explicit + detach requests via FltDetachVolume or FilterDetach will always be + failed. + +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(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgInstanceQueryTeardown: Entered\n") ); + + return STATUS_SUCCESS; +} + + +VOID +CgInstanceTeardownStart ( + _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. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgInstanceTeardownStart: Entered\n") ); +} + + +VOID +CgInstanceTeardownComplete ( + _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 ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgInstanceTeardownComplete: Entered\n") ); +} + + +/************************************************************************* + MiniFilter initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all 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 the final status of this operation. + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( RegistryPath ); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] DriverEntry: Entered\n") ); + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &gFilterInstance ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering i/o + // + + status = FltStartFiltering( gFilterInstance ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( gFilterInstance ); + } + } + + return status; +} + +NTSTATUS +CgUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter 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(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgUnload: Entered\n") ); + + + FltUnregisterFilter( gFilterInstance ); + gFilterInstance = NULL; + + return STATUS_SUCCESS; +} + + +/************************************************************************* + Local utility routines. +*************************************************************************/ + +BOOLEAN +CgOperationsNeedDirty ( + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This identifies those operations we need to set the file to be dirty. + This is non-pageable because it could be called on the paging path + +Arguments: + + Data - Pointer to the filter callbackData that is passed to us. + +Return Value: + + TRUE - If we want the file associated with the request to be dirty. + FALSE - If we don't + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + + // + // In this example, we only care about the "contents" of the file. + // The dirty concept depends on what you care about. If you care + // about the file metadata, for example, then you should have to add + // the operations that modify the file metadata as well. + // + + switch(iopb->MajorFunction) { + + case IRP_MJ_WRITE: + return TRUE; + + case IRP_MJ_FILE_SYSTEM_CONTROL: + switch ( iopb->Parameters.FileSystemControl.Common.FsControlCode ) { + case FSCTL_OFFLOAD_WRITE: + case FSCTL_WRITE_RAW_ENCRYPTED: + case FSCTL_SET_ZERO_DATA: + return TRUE; + default: break; + } + break; + + case IRP_MJ_SET_INFORMATION: + switch ( iopb->Parameters.SetFileInformation.FileInformationClass ) { + case FileEndOfFileInformation: + case FileValidDataLengthInformation: + return TRUE; + default: break; + } + break; + default: + break; + } + return FALSE; +} + +NTSTATUS +CgQueryTransactionOutcome( + _In_ PKTRANSACTION Transaction, + _Out_ PULONG TxOutcome + ) +/*++ + +Routine Description: + + This is a helper function that qeury the KTM that how trasnaction was ended. + +Arguments: + + Transaction - Pointer to transaction object. + + TxOutcome - Output. Specifies the type of transaction outcome. + +Return Value: + + The status of the operation +--*/ +{ + HANDLE transactionHandle; + NTSTATUS status; + TRANSACTION_BASIC_INFORMATION txBasicInfo = {0}; + + PAGED_CODE(); + + status = ObOpenObjectByPointer( Transaction, + OBJ_KERNEL_HANDLE, + NULL, + GENERIC_READ, + *TmTransactionObjectType, + KernelMode, + &transactionHandle ); + + if (!NT_SUCCESS(status)) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgQueryTransactionOutcome: ObOpenObjectByPointer failed.\n") ); + return status; + } + + status = ZwQueryInformationTransaction( transactionHandle, + TransactionBasicInformation, + &txBasicInfo, + sizeof(TRANSACTION_BASIC_INFORMATION), + NULL ); + if (!NT_SUCCESS(status)) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgQueryTransactionOutcome: ObOpenObjectByPointer failed.\n") ); + goto Cleanup; + } + + *TxOutcome = txBasicInfo.Outcome; + +Cleanup: + + ZwClose(transactionHandle); + + return status; +} + +FORCEINLINE +VOID +CgPropagateDirty( + _Inout_ PCG_FILE_CONTEXT FileContext, + _In_ ULONG TransactionOutcome + ) +{ + + if (TransactionOutcome == TransactionOutcomeCommitted) { + + // + // The 'or' operator here handles the case below: + // + // It is possible that the user only read the file even if it opens the file transacted file read and write. + // So, fileContext->TxDirty is possible to be FALSE. + // + // Since KTM callback is asynchrounous, notifications are not necessarily received in order. + // It is possible that dirty information will be wiped out if we use + // + // fileContext->Dirty = fileContext->TxDirty; + // + + FileContext->Dirty |= FileContext->TxDirty; + } + + // + // Clear TxDirty regardless of transaction outcome. + // + + FileContext->TxDirty = FALSE; +} + +NTSTATUS +CgProcessPreviousTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PCG_FILE_CONTEXT FileContext + ) +/*++ + +Routine Description: + + This routine is transaction related implmentation, and is expected to be + invoked at post-create. Note that this function will enlist the newly + allocated transaction context via FltEnlistInTransaction if it needs to. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + FileContext - The file context. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + ULONG txOutcome = TransactionOutcomeUndetermined; + NTSTATUS status = STATUS_SUCCESS; + PCG_TRANSACTION_CONTEXT oldTxCtx = NULL; + PCG_TRANSACTION_CONTEXT transactionContext = NULL; + + PAGED_CODE(); + + if (FltObjects->Transaction != NULL) { + + // + // Get transaction context + // + + status = CgFindOrCreateTransactionContext( FltObjects, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgProcessPreviousTransaction: CgFindOrCreateTransactionContext FAILED\n") ); + transactionContext = NULL; + goto Cleanup; + } + + // + // Enlist it if haven't. + // + + if (!transactionContext->Enlisted) { + + status = FltEnlistInTransaction( FltObjects->Instance, + FltObjects->Transaction, + transactionContext, + TRANSACTION_NOTIFY_COMMIT_FINALIZE | TRANSACTION_NOTIFY_ROLLBACK ); + + if (!NT_SUCCESS( status ) && (status != STATUS_FLT_ALREADY_ENLISTED) ) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgProcessPreviousTransaction: FltEnlistInTransaction FAILED!!!!\n") ); + goto Cleanup; + } + + status = STATUS_SUCCESS; + transactionContext->Enlisted = TRUE; + } + } + + // + // Here we have five cases: + // + // 1) + // oldTxCtx : NULL + // transCtx : B + // 2) + // oldTxCtx : A + // transCtx : NULL + // 3) + // oldTxCtx : A + // transCtx : B + // 4) + // oldTxCtx : A + // transCtx : A + // 5) + // oldTxCtx : NULL + // transCtx : NULL + // + + // + // Synchronize the replacement of FileContext->TxContext with KTM callback. + // + + oldTxCtx = InterlockedExchangePointer( &FileContext->TxContext, transactionContext ); + + if (oldTxCtx != transactionContext) { // case 1,2,3 + + if ( oldTxCtx == NULL ) { // case 1 + + // + // Since we exchanged the pointer, we need to increment the referece count + // + + FltReferenceContext ( transactionContext ); + + // + // Before insertion into the FcList in transaction context, we increment file context's ref count + // + + ExAcquireFastMutex( transactionContext->Mutex ); + + if (!transactionContext->ListDrained) { + + FltReferenceContext ( FileContext ); // Q + InsertTailList( &transactionContext->ScListHead, + &FileContext->ListInTransaction ); + } + + ExReleaseFastMutex( transactionContext->Mutex ); + + goto Cleanup; + } + + // case 2,3 + + // + // There can only be one transacted writer for the file so the previous + // transaction must have finished. Whether or not the TxDirty state is propagated + // depends on its outcome so query that now. + // + + status = CgQueryTransactionOutcome( oldTxCtx->Transaction, &txOutcome ); + + if (!NT_SUCCESS( status )) { + + // + // We have exchanged the transaction pointer already. If we can't get the outcome, + // we have to proceed anyway. + // + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgProcessPreviousTransaction: CgQueryTransactionOutcome FAILED!!!!\n") ); + } + + // + // Remove the file context from the original transaction context. + // + + ExAcquireFastMutex( oldTxCtx->Mutex ); + RemoveEntryList ( &FileContext->ListInTransaction ); + ExReleaseFastMutex( oldTxCtx->Mutex ); + + CgPropagateDirty ( FileContext, txOutcome ); + + if ( transactionContext ) { // case 3 + + FltReferenceContext( transactionContext ); + + ExAcquireFastMutex( transactionContext->Mutex ); + + if (!transactionContext->ListDrained) { + + InsertTailList( &transactionContext->ScListHead, + &FileContext->ListInTransaction ); + + } else { + + FltReleaseContext( FileContext ); + } + + ExReleaseFastMutex( transactionContext->Mutex ); + + } else { // case 2 + + FltReleaseContext ( FileContext ); // Release reference count at Q + } + + // case 2,3 + + FltReleaseContext( oldTxCtx ); // Release reference count in file context originally. + + } + // + // We don't care about case 4, 5. + // + +Cleanup: + + if (transactionContext) { + + FltReleaseContext( transactionContext ); // Release the ref count grabbed at CgFindOrCreateTransactionContext(...) + } + + return status; +} + +NTSTATUS +CgProcessTransactionOutcome( + _Inout_ PCG_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG TransactionOutcome + ) +/*++ + +Routine Description: + + This is a helper function that process transaction commitment or rollback + +Arguments: + + TransactionContext - Pointer to the minifilter driver's transaction context + set at PostCreate. + + TransactionOutcome - Specifies the type of notifications. Should be either + TransactionOutcomeCommitted or TransactionOutcomeAborted + +Return Value: + + STATUS_SUCCESS - Returning this status value indicates that the minifilter + driver is finished with the transaction. This is a success code. + +--*/ +{ + PLIST_ENTRY scan; + PLIST_ENTRY next; + PCG_FILE_CONTEXT fileContext = NULL; + PCG_TRANSACTION_CONTEXT oldTxCtx = NULL; + + PAGED_CODE(); + + // + // Tranversing the file context list, and + // sync the TxDirty -> Dirty. + // + // Either commit or rollback, we need to cleanup the list + // Tear down file context list inside transactionContext + // + + ExAcquireFastMutex( TransactionContext->Mutex ); + + LIST_FOR_EACH_SAFE( scan, next, &TransactionContext->ScListHead ) { + + fileContext = CONTAINING_RECORD( scan, CG_FILE_CONTEXT, ListInTransaction ); + oldTxCtx = InterlockedCompareExchangePointer( &fileContext->TxContext, NULL, TransactionContext ); + if (oldTxCtx == TransactionContext) { + + // + // When oldTxCtx and TransactionContext are equal, it means that + // fileContext->TxContext has been successfully set to NULL. + // + + RemoveEntryList ( scan ); + CgPropagateDirty( fileContext, TransactionOutcome ); + FltReleaseContext( oldTxCtx ); + + // + // This sample demonstrates how we propagate TxDirty to Dirty. + // If the file becomes dirty, we print out here. + // + + if (fileContext->Dirty) { + + CG_DBG_PRINT( CGDBG_TRACE_DEBUG, + ("[CG] CgProcessTransactionOutcome: Transacted file ID %I64x,%I64x is dirty\n", + fileContext->FileID.FileId64.UpperZeroes, + fileContext->FileID.FileId64.Value) ); + } + + FltReleaseContext( fileContext ); + } + } + TransactionContext->ListDrained = TRUE; + ExReleaseFastMutex( TransactionContext->Mutex ); + + + + return STATUS_SUCCESS; +} + +/************************************************************************* + MiniFilter callback routines. +*************************************************************************/ + +FLT_PREOP_CALLBACK_STATUS +CgPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the registered callback routine for filtering + the "write" operation, i.e. the operations that have potential + to make the file dirty. + + This is non-pageable because it could be called on the paging path + +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 - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status; + PCG_FILE_CONTEXT fileContext = NULL; + + UNREFERENCED_PARAMETER( CompletionContext ); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPreOperationCallback: Entered\n") ); + + if (!CgOperationsNeedDirty(Data)) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + status = FltGetFileContext( FltObjects->Instance, + FltObjects->FileObject, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPreOperationCallback: get file context failed. rq: %d\n", + Data->Iopb->MajorFunction) ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // If this operation is performed in a transacted writer view. + // + + if (fileContext->TxContext != NULL) { + +#if DBG + PCG_TRANSACTION_CONTEXT transactionContext = NULL; + + NTSTATUS statusTx = FltGetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + FLT_ASSERTMSG( "Transaction context should not fail, because it is supposed to be created at post create.\n", NT_SUCCESS( statusTx )); + FLT_ASSERTMSG( "The file's TxCtx should be identical with the target TxCtx.\n", + fileContext->TxContext == transactionContext); + + if (NT_SUCCESS( statusTx )) { + FltReleaseContext( transactionContext ); + } + +#endif // DBG + + // + // Instead of updating Dirty, we update TxDirty here, + // because this modification is occurring in the context of transaction + // so if the transaction rolls back then the file will not be set as + // Dirty, i.e. the dirty will not be propagated from TxDirty to Dity. + // This is why we have TxDirty here. + // + + fileContext->TxDirty = TRUE; + + } else { + + fileContext->Dirty = TRUE; + } + + FltReleaseContext( fileContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + +FLT_PREOP_CALLBACK_STATUS +CgPreFsControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre-file system control callback. This filter example does not support save point feature. + So, we explicitly fail the request here. + +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 - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + PAGED_CODE(); + + if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_TXFS_SAVEPOINT_INFORMATION ) { + + // + // We explicitly fail the request of save point here. + // + + Data->IoStatus.Status = STATUS_NOT_SUPPORTED; + return FLT_PREOP_COMPLETE; + } + return CgPreOperationCallback(Data, FltObjects, CompletionContext); +} + + +FLT_PREOP_CALLBACK_STATUS +CgPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-create completion routine. + In this routine, file context and/or transaction context shall be + created if not exits. + +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 - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + FLT_PREOP_SYNCHRONIZE + +--*/ +{ + + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPreOperationCallback: Entered\n") ); + + // + // Return FLT_PREOP_SYNCHRONIZE at PreCreate due to + // some callback of PostCreate may be at DPC level. + // eResource is required at level < DPC. + // + + return FLT_PREOP_SYNCHRONIZE; + +} + +FLT_POSTOP_CALLBACK_STATUS +CgPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-create completion routine. + In this routine, file context and/or transaction context shall be + created if not exits. + +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 completion context set in the pre-create routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + NTSTATUS status = Data->IoStatus.Status; + PCG_FILE_CONTEXT fileContext = NULL; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + if (!NT_SUCCESS( status ) || + (status == STATUS_REPARSE)) { + + // + // File creation may fail. + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPostCreate: file creation failed\n") ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Find or create a file context + // + + status = CgFindOrCreateFileContext( Data, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + // + // In this filter sample, if creation or retrieval of the contexts fails, + // we let the creation go through because this example focuses on being + // an non-intrusive filter. However, if tracking is critical for your + // filter, then you should fail the create via FltCancelFileOpen(...). + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPostCreate: find file context failed. \n") ); + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // If successfully opened a file with the desired access matching + // the "exclusive write" from a TxF point of view, we can know that + // if previous transaction context exists, it must have been comitted + // or rollbacked. + // + + if (FlagOn( Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess, + FILE_WRITE_DATA | FILE_APPEND_DATA | + DELETE | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | + WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY ) ) { + + status = CgProcessPreviousTransaction ( FltObjects, + fileContext ); + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG] CgPostCreate: CgProcessTransaction FAILED!! \n") ); + + goto Cleanup; + } + } + + +Cleanup: + + FltReleaseContext( fileContext ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +CgPreClose ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre-close callback. Make the file context persistent in the volatile cache. + If the file is transacted, it will be synced at KTM notification callback + if committed. + +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 - If this callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK or + FLT_PREOP_SYNCHRONIZE, this parameter is an optional context pointer to be passed to + the corresponding post-operation callback routine. Otherwise, it must be NULL. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + + NTSTATUS status; + PCG_FILE_CONTEXT fileContext = NULL; + + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + status = FltGetFileContext( FltObjects->Instance, + FltObjects->FileObject, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgPreClose: find file context failed.\n") ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // For non-transacted files, + // we just print out the file is dirty or not + // + + if ((FltObjects->Transaction == NULL) && + fileContext->Dirty) { + + CG_DBG_PRINT( CGDBG_TRACE_DEBUG, + ("[CG] CgPreClose: Non-transacted file ID %I64x,%I64x is dirty\n", + fileContext->FileID.FileId64.UpperZeroes, + fileContext->FileID.FileId64.Value) ); + } + + FltReleaseContext( fileContext ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + +NTSTATUS +CgKtmNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ) +/*++ + +Routine Description: + + The registered routine of type PFLT_TRANSACTION_NOTIFICATION_CALLBACK + in FLT_REGISTRATION structure. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + TransactionContext - Pointer to the minifilter driver's transaction context + set at PostCreate. + + TransactionNotification - Specifies the type of notifications that the + filter manager is sending to the minifilter driver. + +Return Value: + + STATUS_SUCCESS - Returning this status value indicates that the minifilter + driver is finished with the transaction. This is a success code. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG] CgKtmNotificationCallback: Entered\n") ); + + FLT_ASSERTMSG("[CG] CgKtmNotificationCallback: The expected type of notifications registered at FltEnlistInTransaction(...).\n", + FlagOn( TransactionNotification, + (TRANSACTION_NOTIFY_COMMIT_FINALIZE | TRANSACTION_NOTIFY_ROLLBACK) ) ); + + if (NULL != TransactionContext) { + + if ( FlagOn( TransactionNotification, TRANSACTION_NOTIFY_COMMIT_FINALIZE ) ) { + + return CgProcessTransactionOutcome( TransactionContext, TransactionOutcomeCommitted ); + + } else { + + return CgProcessTransactionOutcome( TransactionContext, TransactionOutcomeAborted ); + } + } + + return STATUS_SUCCESS; +} + diff --git a/filesys/miniFilter/change/change.h b/filesys/miniFilter/change/change.h new file mode 100644 index 00000000..b8c8c9f2 --- /dev/null +++ b/filesys/miniFilter/change/change.h @@ -0,0 +1,50 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + change.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. Mainly used by change module. + +Environment: + + Kernel mode + +--*/ +#ifndef __CHANGE_H__ +#define __CHANGE_H__ + +#define CG_VISTA (NTDDI_VERSION >= NTDDI_VISTA) + +#include <fltKernel.h> +#include <suppress.h> +#include "context.h" +#include "utility.h" + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +// +// The global variable +// + +PFLT_FILTER gFilterInstance; + +#define CGDBG_TRACE_ROUTINES 0x00000001 +#define CGDBG_TRACE_OPERATION_STATUS 0x00000002 +#define CGDBG_TRACE_DEBUG 0x00000004 +#define CGDBG_TRACE_ERROR 0x00000008 + +static ULONG gTraceFlags = CGDBG_TRACE_DEBUG | CGDBG_TRACE_ERROR; + +#define CG_DBG_PRINT( _dbgLevel, _string ) \ + (FlagOn(gTraceFlags,(_dbgLevel)) ? \ + DbgPrint _string : \ + ((int)0)) + +#endif diff --git a/filesys/miniFilter/change/change.inf b/filesys/miniFilter/change/change.inf new file mode 100644 index 00000000..71d602fb --- /dev/null +++ b/filesys/miniFilter/change/change.inf @@ -0,0 +1,95 @@ +;;; +;;; Change +;;; +;;; +;;; Copyright (c) Microsoft Corporation. All Rights Reserved +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2011,1.0.0.1 +CatalogFile = change.cat + + +[DestinationDirs] +DefaultDestDir = 12 +Change.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = Change.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,Change.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = Change.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[Change.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = Change.AddRegistry + +; +; Registry Modifications +; + +[Change.AddRegistry] +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[Change.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +change.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "A File Change Monitoring Mini-Filter Driver" +ServiceName = "change" +DriverName = "change" +DiskId1 = "File Change Monitoring Device Installation Disk" + +;Instances specific information. +DefaultInstance = "change Instance" +Instance1.Name = "change Instance" +Instance1.Altitude = "370160" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/change/change.rc b/filesys/miniFilter/change/change.rc new file mode 100644 index 00000000..b36223cd --- /dev/null +++ b/filesys/miniFilter/change/change.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "File Change Monitoring Filter Driver" +#define VER_INTERNALNAME_STR "change.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/change/change.sln b/filesys/miniFilter/change/change.sln new file mode 100644 index 00000000..98e886ed --- /dev/null +++ b/filesys/miniFilter/change/change.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "change", "change.vcxproj", "{0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|Win32.Build.0 = Debug|Win32 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|Win32.ActiveCfg = Release|Win32 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|Win32.Build.0 = Release|Win32 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|x64.ActiveCfg = Debug|x64 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Debug|x64.Build.0 = Debug|x64 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|x64.ActiveCfg = Release|x64 + {0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/change/change.vcxproj b/filesys/miniFilter/change/change.vcxproj new file mode 100644 index 00000000..d16c83bf --- /dev/null +++ b/filesys/miniFilter/change/change.vcxproj @@ -0,0 +1,181 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{0E6C9EB8-07AE-4E07-BEFB-D7C0E98D8442}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{1794254B-AC98-41EB-A845-110FCD1AA932}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>change</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>change</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>change</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>change</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="change.c" /> + <ClCompile Include="context.c" /> + <ResourceCompile Include="change.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/change/change.vcxproj.Filters b/filesys/miniFilter/change/change.vcxproj.Filters new file mode 100644 index 00000000..1bca247f --- /dev/null +++ b/filesys/miniFilter/change/change.vcxproj.Filters @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{D9D46B85-B0A5-4C33-8BE4-28566220F42D}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{B66FFE81-FB9C-4422-BF7B-795BA16B48F1}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{78FB7669-0DC6-4483-9EAB-571950829035}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{2E70873B-AF69-4324-ACF7-7318A567F6B3}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="change.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="context.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="change.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/change/context.c b/filesys/miniFilter/change/context.c new file mode 100644 index 00000000..b15ebac4 --- /dev/null +++ b/filesys/miniFilter/change/context.c @@ -0,0 +1,609 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + context.c + +Abstract: + + Filter Context-related module implementation. + +Environment: + + Kernel mode + +--*/ + +#include "change.h" + +// +// Local function prototypes. +// + +NTSTATUS +CgCreateFileContext ( + _Outptr_ PCG_FILE_CONTEXT *FileContext + ); + +VOID +CgFileContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +CgTransactionContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CgCreateFileContext) +#pragma alloc_text(PAGE, CgFindOrCreateFileContext) +#pragma alloc_text(PAGE, CgFindOrCreateTransactionContext) +#pragma alloc_text(PAGE, CgFileContextCleanup) +#pragma alloc_text(PAGE, CgTransactionContextCleanup) +#endif + +// +// Context registration structure +// + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_FILE_CONTEXT, + 0, + CgFileContextCleanup, + CG_FILE_CONTEXT_SIZE, + CG_FILE_CONTEXT_TAG }, + + { FLT_TRANSACTION_CONTEXT, + 0, + CgTransactionContextCleanup, + CG_TRANSACTION_CONTEXT_SIZE, + CG_TRANSACTION_CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + + +VOID +CgFileContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine is called whenever the file context is about to be destroyed. + Typically we need to clean the data structure inside it. + +Arguments: + + Context - Pointer to the PCG_FILE_CONTEXT data structure. + + ContextType - This value should be FLT_FILE_CONTEXT. + +Return Value: + + None + +--*/ +{ + PCG_FILE_CONTEXT fileContext = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ContextType ); + + fileContext = (PCG_FILE_CONTEXT) Context; + + FLT_ASSERTMSG( "[CG]: File context is not supposed to be in the transaction context list at cleanup.!\n", + NULL == fileContext->TxContext ); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Cleaning up file context for file ID %I64x,%I64x (FileContext = %p), dirty = %d\n", + fileContext->FileID.FileId64.UpperZeroes, + fileContext->FileID.FileId64.Value, + fileContext, + fileContext->Dirty) ); + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: File context cleanup complete.\n") ); + + +} + +VOID +CgTransactionContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine is called whenever the file context is about to be destroyed. + Typically we need to clean the data structure inside it. + +Arguments: + + Context - Pointer to the PCG_TRANSACTION_CONTEXT data structure. + + ContextType - This value should be FLT_TRANSACTION_CONTEXT. + +Return Value: + + None + +--*/ +{ + PCG_TRANSACTION_CONTEXT transactionContext = (PCG_TRANSACTION_CONTEXT) Context; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ContextType ); + + CG_DBG_PRINT( CGDBG_TRACE_DEBUG, + ("[CG]: CgTransactionContextCleanup context cleanup entered.\n") ); + + CgFreeMutex( transactionContext->Mutex ); + transactionContext->Mutex = NULL; + ObDereferenceObject( transactionContext->Transaction ); + transactionContext->Transaction = NULL; +} + +NTSTATUS +CgGetFileId ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PCG_FILE_REFERENCE FileId + ) +/*++ + +Routine Description: + + This routine gets the File ID, given a file object. It deals with both, + the 128-bit (ReFS) and 64-bits FileIDs. + +Arguments: + + Instance - Opaque filter pointer for the caller. This parameter is required and cannot be NULL. + + FileObject - File object pointer for the file. This parameter is required and cannot be NULL. + + FileId - Pointer to file id. This is the output + +Return Value: + + Returns status forwarded from FltQueryInformationFile. + On success, FileId will hold the FileID for the file. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FLT_FILESYSTEM_TYPE type; + + // + // Query for what type of filesystem we are sitting on top of. + // + + status = FltGetFileSystemType( Instance, &type ); + + if (NT_SUCCESS( status )) { + + if (type == FLT_FSTYPE_REFS) { + + FILE_ID_INFORMATION fileIdInformation; + + status = FltQueryInformationFile( Instance, + FileObject, + &fileIdInformation, + sizeof(FILE_ID_INFORMATION), + FileIdInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + RtlCopyMemory(&FileId->FileId128, &fileIdInformation.FileId, sizeof(FileId->FileId128) ); + } + + } else { + + FILE_INTERNAL_INFORMATION fileInternalInformation; + + status = FltQueryInformationFile( Instance, + FileObject, + &fileInternalInformation, + sizeof(FILE_INTERNAL_INFORMATION), + FileInternalInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + FileId->FileId64.Value = fileInternalInformation.IndexNumber.QuadPart; + FileId->FileId64.UpperZeroes = 0LL; + } + } + } + + return status; +} + +NTSTATUS +CgFindOrCreateFileContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _Outptr_ PCG_FILE_CONTEXT *FileContext + ) +/*++ + +Routine Description: + + This routine finds the file context for the target file. + If the context does not exist this routing creates + a new one and attaches the context to the file. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + FileContext - Returns the file context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCG_FILE_CONTEXT fileContext; + PCG_FILE_CONTEXT oldFileContext; + + PAGED_CODE(); + + *FileContext = NULL; + + // + // First try to get the file context. + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Trying to get file context (FileObject = %p, Instance = %p, rq = %d)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance, + Cbd->Iopb->MajorFunction) ); + + status = FltGetFileContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + &fileContext ); + + // + // If the call failed because the context does not exist + // and the user wants to creat a new one, then create a + // new context + // + + if (status == STATUS_NOT_FOUND) { + + CG_FILE_REFERENCE fileID; + + status = CgGetFileId( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + &fileID ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to get file id with status 0x%x. (FileObject = %p, Instance = %p, rq = %d)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance, + Cbd->Iopb->MajorFunction) ); + + return status; + + } + + // + // Create a file context + // + + status = CgCreateFileContext( &fileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to create file context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Initiailize fileContext + // + + RtlCopyMemory( &fileContext->FileID, &fileID, sizeof(fileContext->FileID) ); + + // + // Set the new context we just allocated on the file object + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Setting file context %p (FileObject = %p, Instance = %p)\n", + fileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltSetFileContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + fileContext, + &oldFileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to set file context with status 0x%x. (FileObject = %p, Instance = %p, rq = %d)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance, + Cbd->Iopb->MajorFunction) ); + // + // We release the context here because FltSetFileContext failed + // + // If FltSetFileContext succeeded then the context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Releasing file context %p (FileObject = %p, Instance = %p)\n", + fileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( fileContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // FltSetFileContext failed for a reason other than the context already + // existing on the file. So the object now does not have any context set + // on it. So we return failure to the caller. + // + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to set file context with status 0x%x != STATUS_FLT_CONTEXT_ALREADY_DEFINED. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Race: File context already defined. Retaining old file context %p (FileObject = %p, Instance = %p)\n", + oldFileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // Return the existing context. Note that the new context that we allocated has already been + // realeased above. + // + + fileContext = oldFileContext; + status = STATUS_SUCCESS; + + } + } + + *FileContext = fileContext; + + return status; +} + + +NTSTATUS +CgCreateFileContext ( + _Outptr_ PCG_FILE_CONTEXT *FileContext + ) +/*++ + +Routine Description: + + This routine creates a new file context + +Arguments: + + FileContext - Returns the file context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCG_FILE_CONTEXT fileContext; + + PAGED_CODE(); + + // + // Allocate a file context + // + + CG_DBG_PRINT( CGDBG_TRACE_ROUTINES, + ("[CG]: Allocating file context \n") ); + + status = FltAllocateContext( gFilterInstance, + FLT_FILE_CONTEXT, + CG_FILE_CONTEXT_SIZE, + PagedPool, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to allocate file context with status 0x%x \n", + status) ); + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory(fileContext, CG_FILE_CONTEXT_SIZE); + *FileContext = fileContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +CgFindOrCreateTransactionContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_ PCG_TRANSACTION_CONTEXT *TransactionContext + ) +/*++ + +Routine Description + + This routine finds the transaction context, if not found, it will + try to create a new one. The caller is responsible for calling + FltReleaseContext to decrement its reference count. + +Arguments + + FltObjects - Contains parameters required to enlist in a transaction. + TransactionContext - Returns the transaction context + +Return value + + Returns STATUS_SUCCESS if we were able to successfully find/create + a transaction context. Returns an appropriate error code on a failure. + +--*/ +{ + NTSTATUS status; + PCG_TRANSACTION_CONTEXT transactionContext = NULL; + PCG_TRANSACTION_CONTEXT oldTransactionContext = NULL; + PFAST_MUTEX pFastMutex = NULL; + + PAGED_CODE(); + + CG_DBG_PRINT( CGDBG_TRACE_DEBUG, + ("[CG]: CgFindOrCreateTransactionContext entered. \n") ); + + FLT_ASSERTMSG( "[CG]: Transaction object pointer is not supposed to be NULL !\n", FltObjects->Transaction != NULL); + + status = FltGetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + if (NT_SUCCESS( status )) { + + *TransactionContext = transactionContext; + return STATUS_SUCCESS; + } + + if (status != STATUS_NOT_FOUND) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to get transaction context with status 0x%x \n", + status) ); + return status; + } + + // + // Allocate the resource + // + + pFastMutex = CgAllocateMutex(); + + if ( NULL == pFastMutex ) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Allocate a transaction context. + // + + status = FltAllocateContext( gFilterInstance, + FLT_TRANSACTION_CONTEXT, + CG_TRANSACTION_CONTEXT_SIZE, + PagedPool, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to allocate transaction context with status 0x%x \n", + status) ); + + CgFreeMutex( pFastMutex ); + return status; + } + + FLT_ASSERTMSG( "[CG]: Transaction object pointer is not supposed to be NULL !\n", FltObjects->Transaction != NULL); + + // + // Initialization of transaction context. + // The reason we allocate eResource seperately is because + // eResource has to be allocated in the non-paged pool. + // + + RtlZeroMemory(transactionContext, CG_TRANSACTION_CONTEXT_SIZE); + transactionContext->Mutex = pFastMutex; + ObReferenceObject( FltObjects->Transaction ); + transactionContext->Transaction = FltObjects->Transaction; + InitializeListHead( &transactionContext->ScListHead ); + ExInitializeFastMutex( transactionContext->Mutex ); + + status = FltSetTransactionContext( FltObjects->Instance, + FltObjects->Transaction, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + transactionContext, + &oldTransactionContext ); + + if (NT_SUCCESS( status )) { + + *TransactionContext = transactionContext; + return STATUS_SUCCESS; + } + + + FltReleaseContext( transactionContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + CG_DBG_PRINT( CGDBG_TRACE_ERROR, + ("[CG]: Failed to set transaction context with status 0x%x \n", + status) ); + + return status; + } + + FLT_ASSERTMSG( "[CG]: if FltSetTransactionContext returns STATUS_FLT_CONTEXT_ALREADY_DEFINED, the pointer should not be NULL.\n", + oldTransactionContext != NULL); + + *TransactionContext = oldTransactionContext; + + + return STATUS_SUCCESS; +} + + diff --git a/filesys/miniFilter/change/context.h b/filesys/miniFilter/change/context.h new file mode 100644 index 00000000..33bc0aa8 --- /dev/null +++ b/filesys/miniFilter/change/context.h @@ -0,0 +1,151 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + context.h + +Abstract: + + Header file which contains context-related data + structures, type definitions, constants, + global variables and function prototypes. + +Environment: + + Kernel mode + +--*/ + +#ifndef __CONTEXT_H__ +#define __CONTEXT_H__ + +#define CG_FILE_CONTEXT_TAG 'cFcG' +#define CG_TRANSACTION_CONTEXT_TAG 'cTcG' + +// +// Defines the transaction context structure +// + +typedef struct _CG_TRANSACTION_CONTEXT { + + // + // Transaction object pointer + // + + PKTRANSACTION Transaction; + + // + // A flag that tracks if it has ben enlisted in transaction + // + + BOOLEAN Enlisted; + + // + // A flag that indicates if the fc list is drained + // + + BOOLEAN ListDrained; + + // + // List head for file context list. + // The list is grown only when transacted writers are part of the + // transaction, i.e. this list contains all file contexts likely + // to be modified in a transaction. + // + + LIST_ENTRY ScListHead; + + // + // Lock used to protect the list. + // + + PFAST_MUTEX Mutex; + +} CG_TRANSACTION_CONTEXT, *PCG_TRANSACTION_CONTEXT; + +#define CG_TRANSACTION_CONTEXT_SIZE sizeof( CG_TRANSACTION_CONTEXT ) + +// +// This is to deal with ReFS' 128-bit file IDs & NTFS' 64-bit FileIDs. +// + +typedef union _CG_FILE_REFERENCE { + + // + // For 64-bit fileIDs the upper 64-bits are always zeroes. + // + + struct { + ULONGLONG Value; + ULONGLONG UpperZeroes; + } FileId64; + + FILE_ID_128 FileId128; + +} CG_FILE_REFERENCE, *PCG_FILE_REFERENCE; + +// +// File context data structure +// + +typedef struct _CG_FILE_CONTEXT { + + // + // File ID, obtained from querying the file system for + // FileInternalInformation or FileIdInformation. + // + + CG_FILE_REFERENCE FileID; + + // + // The flag that we use to record if the file is dirty + // if we have seen it before. + // + + BOOLEAN Dirty; + + // + // TxDirty is to record if the file is dirty in a + // transaction + // + + BOOLEAN TxDirty; + + // + // A pointer to the transaction context, so we can jump to list in the transaction. + // + + PCG_TRANSACTION_CONTEXT TxContext; + + // + // This list entry is exactly the embedded entry to + // form a doubly linked list inside transaction context. + // + + LIST_ENTRY ListInTransaction; + +} CG_FILE_CONTEXT, *PCG_FILE_CONTEXT; + +#define CG_FILE_CONTEXT_SIZE sizeof( CG_FILE_CONTEXT ) + + + + +NTSTATUS +CgFindOrCreateFileContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _Outptr_ PCG_FILE_CONTEXT *FileContext + ); + + +NTSTATUS +CgFindOrCreateTransactionContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_ PCG_TRANSACTION_CONTEXT *TransactionContext + ); + + +#endif + diff --git a/filesys/miniFilter/change/utility.h b/filesys/miniFilter/change/utility.h new file mode 100644 index 00000000..bb7743ad --- /dev/null +++ b/filesys/miniFilter/change/utility.h @@ -0,0 +1,58 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Module Name: + + utility.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. The functions include + generic table routines. + +Environment: + + Kernel mode + +--*/ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +#define CG_MUTEX_TAG 'tMgC' + +FORCEINLINE +PFAST_MUTEX +CgAllocateMutex ( + VOID + ) +{ + // + // Fast mutex by its rule has to be in the non-paged pool + // + + return ExAllocatePoolWithTag( NonPagedPoolNx, + sizeof( FAST_MUTEX ), + CG_MUTEX_TAG ); +} + +FORCEINLINE +VOID +CgFreeMutex ( + _In_ PFAST_MUTEX Mutex + ) +{ + + ExFreePoolWithTag( Mutex, + CG_MUTEX_TAG ); +} + +#define LIST_FOR_EACH_SAFE(curr, n, head) \ + for (curr = (head)->Flink , n = curr->Flink ; curr != (head); \ + curr = n, n = curr->Flink ) + + +#endif + diff --git a/filesys/miniFilter/ctx/CtxInit.c b/filesys/miniFilter/ctx/CtxInit.c new file mode 100644 index 00000000..2654b9f9 --- /dev/null +++ b/filesys/miniFilter/ctx/CtxInit.c @@ -0,0 +1,908 @@ +/*++ + +Copyright (c) 1999 - 2003 Microsoft Corporation + +Module Name: + + ContextInit.c + +Abstract: + + This is the main module of the kernel mode filter driver implementing + the context sample. + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + +// +// Global variables +// + +CTX_GLOBAL_DATA Globals; + + +// +// Local function prototypes +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +CtxUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +VOID +CtxContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +CtxInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +CtxInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +CtxInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +CtxInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +#if DBG + +VOID +CtxInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ); + +#endif + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) + +#if DBG +#pragma alloc_text(INIT, CtxInitializeDebugLevel) +#endif + +#pragma alloc_text(PAGE, CtxUnload) +#pragma alloc_text(PAGE, CtxContextCleanup) +#pragma alloc_text(PAGE, CtxInstanceSetup) +#pragma alloc_text(PAGE, CtxInstanceQueryTeardown) +#pragma alloc_text(PAGE, CtxInstanceTeardownStart) +#pragma alloc_text(PAGE, CtxInstanceTeardownComplete) +#endif + + +// +// Filters callback routines +// + +FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + CtxPreCreate, + CtxPostCreate }, + + { IRP_MJ_CLEANUP, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + CtxPreCleanup, + NULL }, + + { IRP_MJ_CLOSE, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + CtxPreClose, + NULL }, + + { IRP_MJ_SET_INFORMATION, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + CtxPreSetInfo, + CtxPostSetInfo }, + + { IRP_MJ_OPERATION_END } +}; + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_INSTANCE_CONTEXT, + 0, + CtxContextCleanup, + CTX_INSTANCE_CONTEXT_SIZE, + CTX_INSTANCE_CONTEXT_TAG }, + + { FLT_FILE_CONTEXT, + 0, + CtxContextCleanup, + CTX_FILE_CONTEXT_SIZE, + CTX_FILE_CONTEXT_TAG }, + + { FLT_STREAM_CONTEXT, + 0, + CtxContextCleanup, + CTX_STREAM_CONTEXT_SIZE, + CTX_STREAM_CONTEXT_TAG }, + + { FLT_STREAMHANDLE_CONTEXT, + 0, + CtxContextCleanup, + CTX_STREAMHANDLE_CONTEXT_SIZE, + CTX_STREAMHANDLE_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 + CtxUnload, // Filters unload routine + CtxInstanceSetup, // InstanceSetup routine + CtxInstanceQueryTeardown, // InstanceQueryTeardown routine + CtxInstanceTeardownStart, // InstanceTeardownStart routine + CtxInstanceTeardownComplete, // 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 ); + + RtlZeroMemory( &Globals, sizeof( Globals ) ); + +#if DBG + + // + // Initialize global debug level + // + + CtxInitializeDebugLevel( RegistryPath ); + +#else + + UNREFERENCED_PARAMETER( RegistryPath ); + +#endif + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Ctx]: Driver being loaded\n") ); + + + + // + // Register with the filter manager + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Start filtering I/O + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( Globals.Filter ); + } + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[Ctx]: Driver loaded complete (Status = 0x%08X)\n", + status) ); + + return status; +} + +#if DBG + +VOID +CtxInitializeDebugLevel ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the filter DebugLevel parameter from + the registry. This value will be found in the registry location + indicated by the RegistryPath passed in. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + Globals.DebugLevel = DEBUG_TRACE_ERROR; + + // + // Open the desired registry key + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (NT_SUCCESS( status )) { + + // + // Read the DebugFlags value from the registry. + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *((PULONG) &(((PKEY_VALUE_PARTIAL_INFORMATION) buffer)->Data)); + } + + // + // Close the registry entry + // + + ZwClose( driverRegKey ); + + } + +} + +#endif + +NTSTATUS +CtxUnload ( + _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( DEBUG_TRACE_LOAD_UNLOAD, + ("[Ctx]: Unloading driver\n") ); + + + FltUnregisterFilter( Globals.Filter ); + Globals.Filter = NULL; + + return STATUS_SUCCESS; +} + +VOID +CtxContextCleanup ( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +{ + PCTX_INSTANCE_CONTEXT instanceContext; + PCTX_FILE_CONTEXT fileContext; + PCTX_STREAM_CONTEXT streamContext; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext; + + PAGED_CODE(); + + switch(ContextType) { + + case FLT_INSTANCE_CONTEXT: + + instanceContext = (PCTX_INSTANCE_CONTEXT) Context; + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Cleaning up instance context for volume %wZ (Context = %p)\n", + &instanceContext->VolumeName, + Context) ); + + // + // Here the filter should free memory or synchronization objects allocated to + // objects within the instance context. The instance context itself should NOT + // be freed. It will be freed by Filter Manager when the ref count on the + // context falls to zero. + // + + CtxFreeUnicodeString( &instanceContext->VolumeName ); + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Instance context cleanup complete.\n") ); + + break; + + + case FLT_FILE_CONTEXT: + + fileContext = (PCTX_FILE_CONTEXT) Context; + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Cleaning up file context for file %wZ (FileContext = %p)\n", + &fileContext->FileName, + fileContext) ); + + + // + // Free the file name + // + + if (fileContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&fileContext->FileName); + } + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: File context cleanup complete.\n") ); + + break; + + case FLT_STREAM_CONTEXT: + + streamContext = (PCTX_STREAM_CONTEXT) Context; + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Cleaning up stream context for file %wZ (StreamContext = %p) \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + &streamContext->FileName, + streamContext, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Delete the resource and memory the memory allocated for the resource + // + + if (streamContext->Resource != NULL) { + + ExDeleteResourceLite( streamContext->Resource ); + CtxFreeResource( streamContext->Resource ); + } + + // + // Free the file name + // + + if (streamContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&streamContext->FileName); + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Stream context cleanup complete.\n") ); + + break; + + case FLT_STREAMHANDLE_CONTEXT: + + streamHandleContext = (PCTX_STREAMHANDLE_CONTEXT) Context; + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Cleaning up stream handle context for file %wZ (StreamContext = %p)\n", + &streamHandleContext->FileName, + streamHandleContext) ); + + // + // Delete the resource and memory the memory allocated for the resource + // + + if (streamHandleContext->Resource != NULL) { + + ExDeleteResourceLite( streamHandleContext->Resource ); + CtxFreeResource( streamHandleContext->Resource ); + } + + // + // Free the file name + // + + if (streamHandleContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&streamHandleContext->FileName); + } + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Stream handle context cleanup complete.\n") ); + + break; + + } + +} + +// +// Instance setup/teardown routines. +// + +NTSTATUS +CtxInstanceSetup ( + _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. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + PCTX_INSTANCE_CONTEXT instanceContext = NULL; + NTSTATUS status = STATUS_SUCCESS; + ULONG volumeNameLength; + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance setup started (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + + // + // Allocate and initialize the context for this volume + // + + + // + // Allocate the instance context + // + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Allocating instance context (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + status = FltAllocateContext( FltObjects->Filter, + FLT_INSTANCE_CONTEXT, + CTX_INSTANCE_CONTEXT_SIZE, + NonPagedPool, + &instanceContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate instance context (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto CtxInstanceSetupCleanup; + } + + // + // Get the NT volume name length + // + + status = FltGetVolumeName( FltObjects->Volume, NULL, &volumeNameLength ); + + if( !NT_SUCCESS( status ) && + (status != STATUS_BUFFER_TOO_SMALL) ) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Unexpected failure in FltGetVolumeName. (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto CtxInstanceSetupCleanup; + } + + // + // Allocate a string big enough to take the volume name + // + + instanceContext->VolumeName.MaximumLength = (USHORT) volumeNameLength; + status = CtxAllocateUnicodeString( &instanceContext->VolumeName ); + + if( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate volume name string. (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto CtxInstanceSetupCleanup; + } + + // + // Get the NT volume name + // + + status = FltGetVolumeName( FltObjects->Volume, &instanceContext->VolumeName, &volumeNameLength ); + + if( !NT_SUCCESS( status ) ) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Unexpected failure in FltGetVolumeName. (Volume = %p, Instance = %p, Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + + goto CtxInstanceSetupCleanup; + } + + + instanceContext->Instance = FltObjects->Instance; + instanceContext->Volume = FltObjects->Volume; + + // + // Set the instance context. + // + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Setting instance context %p for volume %wZ (Volume = %p, Instance = %p)\n", + instanceContext, + &instanceContext->VolumeName, + FltObjects->Volume, + FltObjects->Instance) ); + + status = FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + instanceContext, + NULL ); + + if( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to set instance context for volume %wZ (Volume = %p, Instance = %p, Status = 0x%08X)\n", + &instanceContext->VolumeName, + FltObjects->Volume, + FltObjects->Instance, + status) ); + goto CtxInstanceSetupCleanup; + } + + +CtxInstanceSetupCleanup: + + // + // If FltAllocateContext suceeded then we MUST release the context, + // irrespective of whether FltSetInstanceContext suceeded or not. + // + // FltAllocateContext increments the ref count by one. + // A successful FltSetInstanceContext increments the ref count by one + // and also associates the context with the file system object + // + // FltReleaseContext decrements the ref count by one. + // + // When FltSetInstanceContext succeeds, calling FltReleaseContext will + // leave the context with a ref count of 1 corresponding to the internal + // reference to the context from the file system structures + // + // When FltSetInstanceContext fails, calling FltReleaseContext will + // leave the context with a ref count of 0 which is correct since + // there is no reference to the context from the file system structures + // + + if ( instanceContext != NULL ) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing instance context %p (Volume = %p, Instance = %p)\n", + instanceContext, + FltObjects->Volume, + FltObjects->Instance) ); + + FltReleaseContext( instanceContext ); + } + + + if (NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance setup complete (Volume = %p, Instance = %p). Filter will attach to the volume.\n", + FltObjects->Volume, + FltObjects->Instance) ); + } else { + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance setup complete (Volume = %p, Instance = %p). Filter will not attach to the volume.\n", + FltObjects->Volume, + FltObjects->Instance) ); + } + + return status; +} + + +NTSTATUS +CtxInstanceQueryTeardown ( + _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( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance query teardown started (Instance = %p)\n", + FltObjects->Instance) ); + + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance query teadown ended (Instance = %p)\n", + FltObjects->Instance) ); + return STATUS_SUCCESS; +} + + +VOID +CtxInstanceTeardownStart ( + _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. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance teardown start started (Instance = %p)\n", + FltObjects->Instance) ); + + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance teardown start ended (Instance = %p)\n", + FltObjects->Instance) ); +} + + +VOID +CtxInstanceTeardownComplete ( + _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. + +--*/ +{ + PCTX_INSTANCE_CONTEXT instanceContext; + NTSTATUS status; + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance teardown complete started (Instance = %p)\n", + FltObjects->Instance) ); + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Getting instance context (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + status = FltGetInstanceContext( FltObjects->Instance, + &instanceContext ); + + if (NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Instance teardown for volume %wZ (Volume = %p, Instance = %p, InstanceContext = %p)\n", + &instanceContext->VolumeName, + FltObjects->Volume, + FltObjects->Instance, + instanceContext) ); + + + // + // Here the filter may perform any teardown of its own structures associated + // with this instance. + // + // The filter should not free memory or synchronization objects allocated to + // objects within the instance context. That should be performed in the + // cleanup callback for the instance context + // + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing instance context %p for volume %wZ (Volume = %p, Instance = %p)\n", + instanceContext, + &instanceContext->VolumeName, + FltObjects->Volume, + FltObjects->Instance) ); + + FltReleaseContext( instanceContext ); + } else { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to get instance context (Volume = %p, Instance = %p Status = 0x%x)\n", + FltObjects->Volume, + FltObjects->Instance, + status) ); + } + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Ctx]: Instance teardown complete ended (Instance = %p)\n", + FltObjects->Instance) ); +} + diff --git a/filesys/miniFilter/ctx/CtxProc.h b/filesys/miniFilter/ctx/CtxProc.h new file mode 100644 index 00000000..06873a23 --- /dev/null +++ b/filesys/miniFilter/ctx/CtxProc.h @@ -0,0 +1,230 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + CtxProc.h + +Abstract: + + This is the header file defining the functions of the kernel mode + filter driver implementing the context sample. + + +Environment: + + Kernel mode + + +--*/ + + +// +// Functions implemented in operations.c +// + +FLT_PREOP_CALLBACK_STATUS +CtxPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +CtxPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_opt_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +CtxPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +CtxPreClose ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + + +FLT_PREOP_CALLBACK_STATUS +CtxPreSetInfo ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +CtxPostSetInfo ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_opt_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + + +// +// Functions implemented in context.c +// + +NTSTATUS +CtxFindOrCreateFileContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN CreateIfNotFound, + _When_( CreateIfNotFound != FALSE, _In_ ) _When_( CreateIfNotFound == FALSE, _In_opt_ ) PUNICODE_STRING FileName, + _Outptr_ PCTX_FILE_CONTEXT *StreamContext, + _Out_opt_ PBOOLEAN ContextCreated + ); + +NTSTATUS +CtxCreateFileContext ( + _In_ PUNICODE_STRING FileName, + _Outptr_ PCTX_FILE_CONTEXT *StreamContext + ); + + +NTSTATUS +CtxFindOrCreateStreamContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN CreateIfNotFound, + _Outptr_ PCTX_STREAM_CONTEXT *StreamContext, + _Out_opt_ PBOOLEAN ContextCreated + ); + +NTSTATUS +CtxCreateStreamContext ( + _Outptr_ PCTX_STREAM_CONTEXT *StreamContext + ); + +NTSTATUS +CtxUpdateNameInStreamContext ( + _In_ PUNICODE_STRING DirectoryName, + _Inout_ PCTX_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +CtxCreateOrReplaceStreamHandleContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN ReplaceIfExists, + _Outptr_ PCTX_STREAMHANDLE_CONTEXT *StreamHandleContext, + _Out_opt_ PBOOLEAN ContextReplaced + ); + +NTSTATUS +CtxCreateStreamHandleContext ( + _Outptr_ PCTX_STREAMHANDLE_CONTEXT *StreamHandleContext + ); + +NTSTATUS +CtxUpdateNameInStreamHandleContext ( + _In_ PUNICODE_STRING DirectoryName, + _Inout_ PCTX_STREAMHANDLE_CONTEXT StreamHandleContext + ); + + +// +// Functions implemented in support.c +// + +_At_(String->Length, _Out_range_(==, 0)) +_At_(String->MaximumLength, _In_) +_At_(String->Buffer, _Pre_maybenull_ _Post_notnull_ _Post_writable_byte_size_(String->MaximumLength)) +NTSTATUS +CtxAllocateUnicodeString ( + _Out_ PUNICODE_STRING String + ); + +_At_(String->Length, _Out_range_(==, 0)) +_At_(String->MaximumLength, _Out_range_(==, 0)) +_At_(String->Buffer, _Pre_notnull_ _Post_null_) +VOID +CtxFreeUnicodeString ( + _Pre_notnull_ PUNICODE_STRING String + ); + + +// +// Resource support +// + +FORCEINLINE +PERESOURCE +CtxAllocateResource ( + VOID + ) +{ + + return ExAllocatePoolWithTag( NonPagedPool, + sizeof( ERESOURCE ), + CTX_RESOURCE_TAG ); +} + +FORCEINLINE +VOID +CtxFreeResource ( + _In_ PERESOURCE Resource + ) +{ + + ExFreePoolWithTag( Resource, + CTX_RESOURCE_TAG ); +} + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +CtxAcquireResourceExclusive ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_exclusive_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + !ExIsResourceAcquiredSharedLite(Resource)); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Acquires_lock_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +CtxAcquireResourceShared ( + _Inout_ _Requires_lock_not_held_(*_Curr_) _Acquires_shared_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + KeEnterCriticalRegion(); + (VOID)ExAcquireResourceSharedLite( Resource, TRUE ); +} + +FORCEINLINE +VOID +_Releases_lock_(_Global_critical_region_) +_Requires_lock_held_(_Global_critical_region_) +_IRQL_requires_max_(APC_LEVEL) +CtxReleaseResource ( + _Inout_ _Requires_lock_held_(*_Curr_) _Releases_lock_(*_Curr_) + PERESOURCE Resource + ) +{ + FLT_ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + FLT_ASSERT(ExIsResourceAcquiredExclusiveLite(Resource) || + ExIsResourceAcquiredSharedLite(Resource)); + + ExReleaseResourceLite(Resource); + KeLeaveCriticalRegion(); +} + + diff --git a/filesys/miniFilter/ctx/CtxStruc.h b/filesys/miniFilter/ctx/CtxStruc.h new file mode 100644 index 00000000..831c08f1 --- /dev/null +++ b/filesys/miniFilter/ctx/CtxStruc.h @@ -0,0 +1,213 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + CtxStruct.h + +Abstract: + + This is the header file defining the data structures used by the kernel mode + filter driver implementing the context sample. + + +Environment: + + Kernel mode + + +--*/ + +// +// Memory Pool Tags +// + +#define CTX_STRING_TAG 'tSxC' +#define CTX_RESOURCE_TAG 'cRxC' +#define CTX_INSTANCE_CONTEXT_TAG 'cIxC' +#define CTX_FILE_CONTEXT_TAG 'cFxC' +#define CTX_STREAM_CONTEXT_TAG 'cSxC' +#define CTX_STREAMHANDLE_CONTEXT_TAG 'cHxC' + + +// +// Context sample filter global data +// + +typedef struct _CTX_GLOBAL_DATA { + + // + // Handle to minifilter returned from FltRegisterFilter() + // + + PFLT_FILTER Filter; + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + +} CTX_GLOBAL_DATA, *PCTX_GLOBAL_DATA; + +extern CTX_GLOBAL_DATA Globals; + + + + +// +// Instance context data structure +// + +typedef struct _CTX_INSTANCE_CONTEXT { + + // + // Instance for this context. + // + + PFLT_INSTANCE Instance; + + // + // Volume associated with this instance. + // + + PFLT_VOLUME Volume; + + // + // Name of the volume associated with this instance. + // + + UNICODE_STRING VolumeName; + +} CTX_INSTANCE_CONTEXT, *PCTX_INSTANCE_CONTEXT; + +#define CTX_INSTANCE_CONTEXT_SIZE sizeof( CTX_INSTANCE_CONTEXT ) + + +// +// File context data structure +// + +typedef struct _CTX_FILE_CONTEXT { + + // + // Name of the file associated with this context. + // + + UNICODE_STRING FileName; + + // + // There is no resource to protect the context since the + // filename in the context is never modified. The filename + // is put in when the context is created and then freed + // with context is cleaned-up + // + +} CTX_FILE_CONTEXT, *PCTX_FILE_CONTEXT; + +#define CTX_FILE_CONTEXT_SIZE sizeof( CTX_FILE_CONTEXT ) + + + +// +// Stream context data structure +// + +typedef struct _CTX_STREAM_CONTEXT { + + // + // Name of the file associated with this context. + // + + UNICODE_STRING FileName; + + // + // Number of times we saw a create on this stream + // + + ULONG CreateCount; + + // + // Number of times we saw a cleanup on this stream + // + + ULONG CleanupCount; + + // + // Number of times we saw a close on this stream + // + + ULONG CloseCount; + + // + // Lock used to protect this context. + // + + PERESOURCE Resource; + +} CTX_STREAM_CONTEXT, *PCTX_STREAM_CONTEXT; + +#define CTX_STREAM_CONTEXT_SIZE sizeof( CTX_STREAM_CONTEXT ) + + + +// +// Stream handle context data structure +// + +typedef struct _CTX_STREAMHANDLE_CONTEXT { + + // + // Name of the file associated with this context. + // + + UNICODE_STRING FileName; + + // + // Lock used to protect this context. + // + + PERESOURCE Resource; + +} CTX_STREAMHANDLE_CONTEXT, *PCTX_STREAMHANDLE_CONTEXT; + +#define CTX_STREAMHANDLE_CONTEXT_SIZE sizeof( CTX_STREAMHANDLE_CONTEXT ) + + +// +// 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_INSTANCE_CONTEXT_OPERATIONS 0x00000008 // Operation on instance context +#define DEBUG_TRACE_FILE_CONTEXT_OPERATIONS 0x00000010 // Operation on file context +#define DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS 0x00000020 // Operation on stream context +#define DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS 0x00000040 // Operation on stream handle context + +#define DEBUG_TRACE_ALL_IO 0x00000080 // All IO operations tracked by this filter + +#define DEBUG_TRACE_ALL 0xFFFFFFFF // All flags + + +#define DebugTrace(Level, Data) \ + if ((Level) & Globals.DebugLevel) { \ + DbgPrint Data; \ + } + + +#else + +#define DebugTrace(Level, Data) {NOTHING;} + +#endif + diff --git a/filesys/miniFilter/ctx/ReadMe.md b/filesys/miniFilter/ctx/ReadMe.md new file mode 100644 index 00000000..e9dd284e --- /dev/null +++ b/filesys/miniFilter/ctx/ReadMe.md @@ -0,0 +1,14 @@ +Ctx File System Minifilter Driver +================================= + +The Ctx minifilter is an example that demonstrates how to attach contexts to instances, files, streams, and stream handles in your minifilter. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *Ctx* minifilter demonstrates how to attach and remove contexts from instances, files, steams, and stream handles. *Ctx* attaches a context whenever one of these objects is created. While attaching a context to a file, the sample also creates a stream and stream handle context. All contexts are ultimately deleted by the filter manager using the callback function that the *Ctx* minifilter provides. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. diff --git a/filesys/miniFilter/ctx/context.c b/filesys/miniFilter/ctx/context.c new file mode 100644 index 00000000..f9770987 --- /dev/null +++ b/filesys/miniFilter/ctx/context.c @@ -0,0 +1,885 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + context.c + +Abstract: + + This is the stream nd stream handle context module of the kernel mode + context sample filter driver + + +Environment: + + Kernel mode + + +--*/ + + +#include "pch.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CtxFindOrCreateFileContext) +#pragma alloc_text(PAGE, CtxCreateFileContext) +#pragma alloc_text(PAGE, CtxFindOrCreateStreamContext) +#pragma alloc_text(PAGE, CtxCreateStreamContext) +#pragma alloc_text(PAGE, CtxUpdateNameInStreamContext) +#pragma alloc_text(PAGE, CtxCreateOrReplaceStreamHandleContext) +#pragma alloc_text(PAGE, CtxCreateStreamHandleContext) +#pragma alloc_text(PAGE, CtxUpdateNameInStreamHandleContext) +#endif + + + + +NTSTATUS +CtxFindOrCreateFileContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN CreateIfNotFound, + _When_( CreateIfNotFound != FALSE, _In_ ) _When_( CreateIfNotFound == FALSE, _In_opt_ ) PUNICODE_STRING FileName, + _Outptr_ PCTX_FILE_CONTEXT *FileContext, + _Out_opt_ PBOOLEAN ContextCreated + ) +/*++ + +Routine Description: + + This routine finds the file context for the target file. + Optionally, if the context does not exist this routing creates + a new one and attaches the context to the file. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + CreateIfNotFound - Supplies if the file context must be created if missing + FileName - Supplies the file name + FileContext - Returns the file context + ContextCreated - Returns if a new context was created + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_FILE_CONTEXT fileContext; + PCTX_FILE_CONTEXT oldFileContext; + + PAGED_CODE(); + + *FileContext = NULL; + if (ContextCreated != NULL) *ContextCreated = FALSE; + + // + // First try to get the file context. + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Trying to get file context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltGetFileContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + &fileContext ); + + // + // If the call failed because the context does not exist + // and the user wants to creat a new one, the create a + // new context + // + + if (!NT_SUCCESS( status ) && + (status == STATUS_NOT_FOUND) && + CreateIfNotFound) { + + + // + // Create a file context + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Creating file context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = CtxCreateFileContext( FileName, &fileContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to create file context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + + // + // Set the new context we just allocated on the file object + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Setting file context %p (FileObject = %p, Instance = %p)\n", + fileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltSetFileContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + fileContext, + &oldFileContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set file context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + // + // We release the context here because FltSetFileContext failed + // + // If FltSetFileContext succeeded then the context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing file context %p (FileObject = %p, Instance = %p)\n", + fileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( fileContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // FltSetFileContext failed for a reason other than the context already + // existing on the file. So the object now does not have any context set + // on it. So we return failure to the caller. + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set file context with status 0x%x != STATUS_FLT_CONTEXT_ALREADY_DEFINED. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: File context already defined. Retaining old file context %p (FileObject = %p, Instance = %p)\n", + oldFileContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // Return the existing context. Note that the new context that we allocated has already been + // realeased above. + // + + fileContext = oldFileContext; + status = STATUS_SUCCESS; + + } else { + + if (ContextCreated != NULL) *ContextCreated = TRUE; + } + } + + *FileContext = fileContext; + + return status; +} + + +NTSTATUS +CtxCreateFileContext ( + _In_ PUNICODE_STRING FileName, + _Outptr_ PCTX_FILE_CONTEXT *FileContext + ) +/*++ + +Routine Description: + + This routine creates a new file context + +Arguments: + + FileName - Supplies the file name + FileContext - Returns the file context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_FILE_CONTEXT fileContext; + + PAGED_CODE(); + + // + // Allocate a file context + // + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: Allocating file context \n") ); + + status = FltAllocateContext( Globals.Filter, + FLT_FILE_CONTEXT, + CTX_FILE_CONTEXT_SIZE, + PagedPool, + &fileContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate file context with status 0x%x \n", + status) ); + return status; + } + + // + // Initialize the newly created context + // + + // + // Allocate and copy off the file name + // + + fileContext->FileName.MaximumLength = FileName->Length; + status = CtxAllocateUnicodeString( &fileContext->FileName ); + if (NT_SUCCESS( status )) { + + RtlCopyUnicodeString( &fileContext->FileName, FileName ); + } + + *FileContext = fileContext; + + return STATUS_SUCCESS; +} + + +NTSTATUS +CtxFindOrCreateStreamContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN CreateIfNotFound, + _Outptr_ PCTX_STREAM_CONTEXT *StreamContext, + _Out_opt_ PBOOLEAN ContextCreated + ) +/*++ + +Routine Description: + + This routine finds the stream context for the target stream. + Optionally, if the context does not exist this routing creates + a new one and attaches the context to the stream. + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + CreateIfNotFound - Supplies if the stream must be created if missing + StreamContext - Returns the stream context + ContextCreated - Returns if a new context was created + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_STREAM_CONTEXT streamContext; + PCTX_STREAM_CONTEXT oldStreamContext; + + PAGED_CODE(); + + *StreamContext = NULL; + if (ContextCreated != NULL) *ContextCreated = FALSE; + + // + // First try to get the stream context. + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Trying to get stream context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltGetStreamContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + &streamContext ); + + // + // If the call failed because the context does not exist + // and the user wants to creat a new one, the create a + // new context + // + + if (!NT_SUCCESS( status ) && + (status == STATUS_NOT_FOUND) && + CreateIfNotFound) { + + + // + // Create a stream context + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Creating stream context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = CtxCreateStreamContext( &streamContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to create stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + + // + // Set the new context we just allocated on the file object + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Setting stream context %p (FileObject = %p, Instance = %p)\n", + streamContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = FltSetStreamContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + streamContext, + &oldStreamContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + // + // We release the context here because FltSetStreamContext failed + // + // If FltSetStreamContext succeeded then the context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing stream context %p (FileObject = %p, Instance = %p)\n", + streamContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( streamContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // FltSetStreamContext failed for a reason other than the context already + // existing on the stream. So the object now does not have any context set + // on it. So we return failure to the caller. + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set stream context with status 0x%x != STATUS_FLT_CONTEXT_ALREADY_DEFINED. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Stream context already defined. Retaining old stream context %p (FileObject = %p, Instance = %p)\n", + oldStreamContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // Return the existing context. Note that the new context that we allocated has already been + // realeased above. + // + + streamContext = oldStreamContext; + status = STATUS_SUCCESS; + + } else { + + if (ContextCreated != NULL) *ContextCreated = TRUE; + } + } + + *StreamContext = streamContext; + + return status; +} + + + + +NTSTATUS +CtxCreateStreamContext ( + _Outptr_ PCTX_STREAM_CONTEXT *StreamContext + ) +/*++ + +Routine Description: + + This routine creates a new stream context + +Arguments: + + StreamContext - Returns the stream context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_STREAM_CONTEXT streamContext; + + PAGED_CODE(); + + // + // Allocate a stream context + // + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: Allocating stream context \n") ); + + status = FltAllocateContext( Globals.Filter, + FLT_STREAM_CONTEXT, + CTX_STREAM_CONTEXT_SIZE, + PagedPool, + &streamContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate stream context with status 0x%x \n", + status) ); + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory( streamContext, CTX_STREAM_CONTEXT_SIZE ); + + streamContext->Resource = CtxAllocateResource(); + if(streamContext->Resource == NULL) { + + FltReleaseContext( streamContext ); + return STATUS_INSUFFICIENT_RESOURCES; + } + ExInitializeResourceLite( streamContext->Resource ); + + *StreamContext = streamContext; + + return STATUS_SUCCESS; +} + + +NTSTATUS +CtxUpdateNameInStreamContext ( + _In_ PUNICODE_STRING DirectoryName, + _Inout_ PCTX_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine updates the name of the target in the supplied stream context + +Arguments: + + DirectoryName - Supplies the directory name + StreamContext - Returns the updated name in the stream context + +Return Value: + + Status + +Note: + + The caller must synchronize access to the context. This routine does no + synchronization + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // Free any existing name + // + + if (StreamContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&StreamContext->FileName); + } + + + // + // Allocate and copy off the directory name + // + + StreamContext->FileName.MaximumLength = DirectoryName->Length; + status = CtxAllocateUnicodeString(&StreamContext->FileName); + if (NT_SUCCESS(status)) { + + RtlCopyUnicodeString(&StreamContext->FileName, DirectoryName); + } + + return status; +} + + + + +NTSTATUS +CtxCreateOrReplaceStreamHandleContext ( + _In_ PFLT_CALLBACK_DATA Cbd, + _In_ BOOLEAN ReplaceIfExists, + _Outptr_ PCTX_STREAMHANDLE_CONTEXT *StreamHandleContext, + _Out_opt_ PBOOLEAN ContextReplaced + ) +/*++ + +Routine Description: + + This routine creates a stream handle context for the target stream + handle. Optionally, if the context already exists, this routine + replaces it with the new context and releases the old context + +Arguments: + + Cbd - Supplies a pointer to the callbackData which + declares the requested operation. + ReplaceIfExists - Supplies if the stream handle context must be + replaced if already present + StreamContext - Returns the stream context + ContextReplaced - Returns if an existing context was replaced + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext; + PCTX_STREAMHANDLE_CONTEXT oldStreamHandleContext; + + PAGED_CODE(); + + *StreamHandleContext = NULL; + if (ContextReplaced != NULL) *ContextReplaced = FALSE; + + // + // Create a stream context + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Creating stream handle context (FileObject = %p, Instance = %p)\n", + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + status = CtxCreateStreamHandleContext( &streamHandleContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to create stream context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // Set the new context we just allocated on the file object + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Setting stream context %p (FileObject = %p, Instance = %p, ReplaceIfExists = %x)\n", + streamHandleContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance, + ReplaceIfExists) ); + + status = FltSetStreamHandleContext( Cbd->Iopb->TargetInstance, + Cbd->Iopb->TargetFileObject, + ReplaceIfExists ? FLT_SET_CONTEXT_REPLACE_IF_EXISTS : FLT_SET_CONTEXT_KEEP_IF_EXISTS, + streamHandleContext, + &oldStreamHandleContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set stream handle context with status 0x%x. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // We release the context here because FltSetStreamContext failed + // + // If FltSetStreamContext succeeded then the context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing stream handle context %p (FileObject = %p, Instance = %p)\n", + streamHandleContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( streamHandleContext ); + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + // + // FltSetStreamContext failed for a reason other than the context already + // existing on the stream. So the object now does not have any context set + // on it. So we return failure to the caller. + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Failed to set stream context with status 0x%x != STATUS_FLT_CONTEXT_ALREADY_DEFINED. (FileObject = %p, Instance = %p)\n", + status, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + return status; + } + + // + // We will reach here only if we have failed with STATUS_FLT_CONTEXT_ALREADY_DEFINED + // and we can fail with that code only if the context already exists and we have used + // the FLT_SET_CONTEXT_KEEP_IF_EXISTS flag + + FLT_ASSERT( ReplaceIfExists == FALSE ); + + // + // Race condition. Someone has set a context after we queried it. + // Use the already set context instead + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Stream context already defined. Retaining old stream context %p (FileObject = %p, Instance = %p)\n", + oldStreamHandleContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + // + // Return the existing context. Note that the new context that we allocated has already been + // realeased above. + // + + streamHandleContext = oldStreamHandleContext; + status = STATUS_SUCCESS; + + } else { + + // + // FltSetStreamContext has suceeded. The new context will be returned + // to the caller. The caller will use the context and then release it + // when he is done with the context. + // + // However, if we have replaced an existing context then we need to + // release the old context so as to decrement the ref count on it. + // + // Note that the memory allocated to the objects within the context + // will be freed in the context cleanup and must not be done here. + // + + if ( ReplaceIfExists && + oldStreamHandleContext != NULL) { + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Releasing old stream handle context %p (FileObject = %p, Instance = %p)\n", + oldStreamHandleContext, + Cbd->Iopb->TargetFileObject, + Cbd->Iopb->TargetInstance) ); + + FltReleaseContext( oldStreamHandleContext ); + if (ContextReplaced != NULL) *ContextReplaced = TRUE; + } + } + + *StreamHandleContext = streamHandleContext; + + return status; +} + + + + + +NTSTATUS +CtxCreateStreamHandleContext ( + _Outptr_ PCTX_STREAMHANDLE_CONTEXT *StreamHandleContext + ) +/*++ + +Routine Description: + + This routine creates a new stream context + +Arguments: + + StreamContext - Returns the stream context + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext; + + PAGED_CODE(); + + // + // Allocate a stream context + // + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: Allocating stream handle context \n") ); + + status = FltAllocateContext( Globals.Filter, + FLT_STREAMHANDLE_CONTEXT, + CTX_STREAMHANDLE_CONTEXT_SIZE, + PagedPool, + &streamHandleContext ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate stream handle context with status 0x%x \n", + status) ); + + return status; + } + + // + // Initialize the newly created context + // + + RtlZeroMemory( streamHandleContext, CTX_STREAMHANDLE_CONTEXT_SIZE ); + + streamHandleContext->Resource = CtxAllocateResource(); + if(streamHandleContext->Resource == NULL) { + + FltReleaseContext( streamHandleContext ); + return STATUS_INSUFFICIENT_RESOURCES; + } + ExInitializeResourceLite( streamHandleContext->Resource ); + + *StreamHandleContext = streamHandleContext; + + return STATUS_SUCCESS; +} + + + +NTSTATUS +CtxUpdateNameInStreamHandleContext ( + _In_ PUNICODE_STRING DirectoryName, + _Inout_ PCTX_STREAMHANDLE_CONTEXT StreamHandleContext + ) +/*++ + +Routine Description: + + This routine updates the name of the target in the supplied stream handle context + +Arguments: + + DirectoryName - Supplies the directory name + StreamHandleContext - Returns the updated name in the stream context + +Return Value: + + Status + +Note: + + The caller must synchronize access to the context. This routine does no + synchronization + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // Free any existing name + // + + if (StreamHandleContext->FileName.Buffer != NULL) { + + CtxFreeUnicodeString(&StreamHandleContext->FileName); + } + + + // + // Allocate and copy off the directory name + // + + StreamHandleContext->FileName.MaximumLength = DirectoryName->Length; + status = CtxAllocateUnicodeString(&StreamHandleContext->FileName); + if (NT_SUCCESS(status)) { + + RtlCopyUnicodeString(&StreamHandleContext->FileName, DirectoryName); + } + + return status; +} + diff --git a/filesys/miniFilter/ctx/ctx.inf b/filesys/miniFilter/ctx/ctx.inf new file mode 100644 index 00000000..9ba06e18 --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.inf @@ -0,0 +1,96 @@ +;;; +;;; Context File System Filter Driver Sample +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = ctx.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugLevel",0x00010001,0x00000001 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +ctx.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Context File System Filter Driver Sample" +ServiceName = "Ctx" +DriverName = "ctx" +DiskId1 = "Ctx Device Installation Disk" + +;Instances specific information. +DefaultInstance = "Ctx" +Instance1.Name = "Ctx" +Instance1.Altitude = "370070" +Instance1.Flags = 0x0 diff --git a/filesys/miniFilter/ctx/ctx.rc b/filesys/miniFilter/ctx/ctx.rc new file mode 100644 index 00000000..19df388b --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Context Sample Mini-Filter" +#define VER_INTERNALNAME_STR "ctx.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/ctx/ctx.sln b/filesys/miniFilter/ctx/ctx.sln new file mode 100644 index 00000000..316647ca --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctx", "ctx.vcxproj", "{8A826E76-F53C-4951-B81A-8653A4FC7BF0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|Win32.ActiveCfg = Debug|Win32 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|Win32.Build.0 = Debug|Win32 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|Win32.ActiveCfg = Release|Win32 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|Win32.Build.0 = Release|Win32 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|x64.ActiveCfg = Debug|x64 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Debug|x64.Build.0 = Debug|x64 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|x64.ActiveCfg = Release|x64 + {8A826E76-F53C-4951-B81A-8653A4FC7BF0}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/ctx/ctx.vcxproj b/filesys/miniFilter/ctx/ctx.vcxproj new file mode 100644 index 00000000..65ce09b0 --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.vcxproj @@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{8A826E76-F53C-4951-B81A-8653A4FC7BF0}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{E0BB0921-D467-4DDA-82CD-8B1FAADDBC55}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>ctx</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>ctx</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>ctx</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>ctx</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="context.c" /> + <ClCompile Include="CtxInit.c" /> + <ClCompile Include="operations.c" /> + <ClCompile Include="support.c" /> + <ResourceCompile Include="ctx.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/ctx/ctx.vcxproj.Filters b/filesys/miniFilter/ctx/ctx.vcxproj.Filters new file mode 100644 index 00000000..4eef3fc4 --- /dev/null +++ b/filesys/miniFilter/ctx/ctx.vcxproj.Filters @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{872ED434-D252-450B-ACD5-B7DDB6FF6D4E}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{FC7A2108-DAAB-43D9-AEFA-120FC32876D7}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{C1AC955C-1C1A-4B66-8AA8-C6173E6B2EDD}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{1E203714-FFC5-4465-BC3D-2ED22ED46D83}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="context.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CtxInit.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="operations.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="support.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ctx.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/ctx/operations.c b/filesys/miniFilter/ctx/operations.c new file mode 100644 index 00000000..743fcd4c --- /dev/null +++ b/filesys/miniFilter/ctx/operations.c @@ -0,0 +1,1080 @@ +/*++ + +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 + context sample + + +Environment: + + Kernel mode + + +--*/ + +#include "pch.h" + + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CtxPreCreate) +#pragma alloc_text(PAGE, CtxPostCreate) +#pragma alloc_text(PAGE, CtxPreCleanup) +#pragma alloc_text(PAGE, CtxPreClose) +#pragma alloc_text(PAGE, CtxPreSetInfo) +#pragma alloc_text(PAGE, CtxPostSetInfo) +#endif + + +FLT_PREOP_CALLBACK_STATUS +CtxPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + UNREFERENCED_PARAMETER( Cbd ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreCreate -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreCreate -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Force a post-op callback so we can add our contexts to the opened + // objects + // + + return FLT_PREOP_SUCCESS_WITH_CALLBACK; + +} + + +FLT_POSTOP_CALLBACK_STATUS +CtxPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_opt_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + + PCTX_FILE_CONTEXT fileContext = NULL; + PCTX_STREAM_CONTEXT streamContext = NULL; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + UNICODE_STRING fileName; + + NTSTATUS status; + BOOLEAN fileContextCreated, streamContextCreated, streamHandleContextReplaced; + + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( CbdContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPostCreate -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + + // + // If the Create has failed, do nothing + // + + if (!NT_SUCCESS( Cbd->IoStatus.Status )) { + + goto CtxPostCreateCleanup; + } + + + // + // Get the file name + // + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_NORMALIZED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to get name information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + + // + // Find or create a stream context + // + + status = CtxFindOrCreateStreamContext(Cbd, + TRUE, + &streamContext, + &streamContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to find or create stream context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Getting/Creating stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + streamContextCreated) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamContext->Resource); + + // + // Increment the create count + // + + streamContext->CreateCount++; + + // + // Update the file name in the context + // + + status = CtxUpdateNameInStreamContext( &nameInfo->Name, + streamContext); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Stream context info for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamContext->Resource); + + // + // Quit on failure after we have given up + // the resource + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to update name in stream context for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + + + // + // Create or replace a stream handle context + // + + status = CtxCreateOrReplaceStreamHandleContext(Cbd, + TRUE, + &streamHandleContext, + &streamHandleContextReplaced); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to find or create stream handle context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Creating/Replacing stream handle context for file %wZ (Cbd = %p, FileObject = %p StreamHandleContext = %p, StreamHandleContextReplaced = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamHandleContext, + streamHandleContextReplaced) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive( streamHandleContext->Resource ); + + // + // Update the file name in the context + // + + status = CtxUpdateNameInStreamHandleContext( &nameInfo->Name, + streamHandleContext); + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Stream handle context info for file %wZ (Cbd = %p, FileObject = %p, StreamHandleContext = %p) \n\tName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamHandleContext, + &streamHandleContext->FileName) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamHandleContext->Resource); + + // + // Quit on failure after we have given up + // the resource + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to update name in stream handle context for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + // + // After FltParseFileNameInformation, nameInfo->Name also + // contains the stream name. We need only the filename and do + // not want to include the stream name in the file context + // + + fileName.Buffer = nameInfo->Name.Buffer; + fileName.Length = nameInfo->Name.Length - nameInfo->Stream.Length; + fileName.MaximumLength = fileName.Length; + + // + // Find or create a file context + // + + status = CtxFindOrCreateFileContext( Cbd, + TRUE, + &fileName, + &fileContext, + &fileContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because file contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Failed to find or create file context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostCreateCleanup; + } + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> Getting/Creating file context for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p. FileContextCreated = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + fileContext, + fileContextCreated) ); + + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostCreate -> File context info for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p) \n\tName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + fileContext, + &fileContext->FileName) ); + + +CtxPostCreateCleanup: + + + // + // Release the references we have acquired + // + + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (fileContext != NULL) { + + FltReleaseContext( fileContext ); + } + + if (streamContext != NULL) { + + FltReleaseContext( streamContext ); + } + + if (streamHandleContext != NULL) { + + FltReleaseContext( streamHandleContext ); + } + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Ctx]: CtxPostCreate -> Failed with status 0x%x \n", + status) ); + + // + // It doesn't make sense to udate Cbd->IoStatus.Status on failure since the + // file system has successfully completed the operation + // + + } + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPostCreate -> Exit (Cbd = %p, FileObject = %p, Status = 0x%x)\n", + Cbd, + FltObjects->FileObject, + Cbd->IoStatus.Status) ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +CtxPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + + PCTX_STREAM_CONTEXT streamContext = NULL; + NTSTATUS status; + BOOLEAN streamContextCreated; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreCleanup -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Get the stream context + // + + status = CtxFindOrCreateStreamContext(Cbd, + FALSE, // do not create if one does not exist + &streamContext, + &streamContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreCleanup -> Failed to find stream context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPreCleanupCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreCleanup -> Getting stream context for file (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", + Cbd, + FltObjects->FileObject, + streamContext, + streamContextCreated) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamContext->Resource); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreCleanup -> Old info in stream context for file(Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + + + // + // Update the cleanup count in the context + // + + streamContext->CleanupCount++; + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreCleanup -> New info in stream context for file (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamContext->Resource); + + +CtxPreCleanupCleanup: + + // + // Release the references we have acquired + // + + if (streamContext != NULL) { + + FltReleaseContext( streamContext ); + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreCleanup -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // It doesn't make sense to fail the cleanup - so ignore any errors we may + // encounter and return success + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + + + + +FLT_PREOP_CALLBACK_STATUS +CtxPreClose ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + + PCTX_STREAM_CONTEXT streamContext = NULL; + NTSTATUS status; + BOOLEAN streamContextCreated; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreClose -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // Get the stream context + // + + status = CtxFindOrCreateStreamContext(Cbd, + FALSE, // do not create if one does not exist + &streamContext, + &streamContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreClose -> Failed to find stream context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPreCloseCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreClose -> Getting stream context for file (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", + Cbd, + FltObjects->FileObject, + streamContext, + streamContextCreated) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamContext->Resource); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreClose -> Old info in stream context for file(Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Update the close count in the context + // + + streamContext->CloseCount++; + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPreClose -> New info in stream context for file (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamContext->Resource); + + +CtxPreCloseCleanup: + + // + // Release the references we have acquired + // + + if (streamContext != NULL) { + + FltReleaseContext( streamContext ); + } + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreClose -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + // + // It doesn't make sense to fail the cleanup - so ignore any errors we may + // encounter and return success + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_PREOP_CALLBACK_STATUS +CtxPreSetInfo ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +{ + FILE_INFORMATION_CLASS fileInformationClass; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreSetInfo -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; // pass through - default is no post op callback + + fileInformationClass = Cbd->Iopb->Parameters.SetFileInformation.FileInformationClass; + + // + // Ignore the ops we do not care about + // + + if ((fileInformationClass != FileRenameInformation)) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreSetInfo -> Ignoring SetInfo operations other than FileRenameInformation (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPreSetInfoCleanup; + } + + // + // We want to process renames in the post-op callback + // + + callbackStatus = FLT_PREOP_SYNCHRONIZE; + + +CtxPreSetInfoCleanup: + + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPreSetInfo -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + + + +FLT_POSTOP_CALLBACK_STATUS +CtxPostSetInfo ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_opt_ PVOID CbdContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +{ + PCTX_INSTANCE_CONTEXT instanceContext = NULL; + PCTX_FILE_CONTEXT fileContext = NULL; + PCTX_STREAM_CONTEXT streamContext = NULL; + PCTX_STREAMHANDLE_CONTEXT streamHandleContext = NULL; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + + NTSTATUS status; + BOOLEAN streamContextCreated, fileContextCreated, streamHandleContextReplaced; + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CbdContext ); + + // + // 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, + ("[Ctx]: CtxPostSetInfo -> Enter (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + + // + // Initialize defaults + // + + status = STATUS_SUCCESS; + + // + // If the SetInfo has failed, do nothing + // + + if (!NT_SUCCESS( Cbd->IoStatus.Status )) { + + goto CtxPostSetInfoCleanup; + } + + + // + // Get the instance context for the target instance + // + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Trying to get instance context (TargetInstance = %p, Cbd = %p, FileObject = %p)\n", + Cbd->Iopb->TargetInstance, + Cbd, + FltObjects->FileObject) ); + + status = FltGetInstanceContext( Cbd->Iopb->TargetInstance, + &instanceContext ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS | DEBUG_TRACE_ERROR, + ("[Ctx]: CtxPostSetInfo -> Failed to get instance context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + DebugTrace( DEBUG_TRACE_INSTANCE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Instance context info for volume %wZ (Cbd = %p, FileObject = %p, InstanceContext = %p) \n\tVolumeName = %wZ \n\tInstance = %p \n\tVolume = %p\n", + &instanceContext->VolumeName, + Cbd, + FltObjects->FileObject, + instanceContext, + &instanceContext->VolumeName, + &instanceContext->Instance, + &instanceContext->Volume) ); + + + // + // Get the directory name + // + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_NORMALIZED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to get file name information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + + // + // Get the stream context + // + + status = CtxFindOrCreateStreamContext(Cbd, + FALSE, // do not create if one does not exist + &streamContext, + &streamContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to find stream context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Getting stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p. StreamContextCreated = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + streamContextCreated) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamContext->Resource); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Old info in stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + + + // + // Update the file name in the context + // + + status = CtxUpdateNameInStreamContext( &nameInfo->Name, + streamContext); + + + DebugTrace( DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> New info in stream context for file %wZ (Cbd = %p, FileObject = %p, StreamContext = %p) \n\tName = %wZ \n\tCreateCount = %x \n\tCleanupCount = %x, \n\tCloseCount = %x\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamContext, + &streamContext->FileName, + streamContext->CreateCount, + streamContext->CleanupCount, + streamContext->CloseCount) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource(streamContext->Resource); + + // + // Quit on failure after we have given up + // the resource + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAM_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to update name in stream context for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + + + // + // Create or replace a stream handle context + // + + status = CtxCreateOrReplaceStreamHandleContext(Cbd, + TRUE, + &streamHandleContext, + &streamHandleContextReplaced); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because stream contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to find or create stream handle context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Creating/Replacing stream handle context for file %wZ (Cbd = %p, FileObject = %p StreamHandleContext = %p, StreamHandleContextReplaced = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamHandleContext, + streamHandleContextReplaced) ); + + // + // Acquire write acccess to the context + // + + CtxAcquireResourceExclusive(streamHandleContext->Resource); + + // + // Update the file name in the context + // + + status = CtxUpdateNameInStreamHandleContext( &nameInfo->Name, + streamHandleContext); + + DebugTrace( DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Stream handle context info for file %wZ (Cbd = %p, FileObject = %p, StreamHandleContext = %p) \n\tName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + streamHandleContext, + &streamHandleContext->FileName) ); + + // + // Relinquish write acccess to the context + // + + CtxReleaseResource( streamHandleContext->Resource ); + + // + // Quit on failure after we have given up + // the resource + // + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_STREAMHANDLE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to update name in stream handle context for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + + // + // Get the file context + // + + status = CtxFindOrCreateFileContext( Cbd, + FALSE, // do not create if one does not exist + NULL, + &fileContext, + &fileContextCreated); + if (!NT_SUCCESS( status )) { + + // + // This failure will most likely be because file contexts are not supported + // on the object we are trying to assign a context to or the object is being + // deleted + // + + DebugTrace( DEBUG_TRACE_ERROR | DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Failed to find file context (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto CtxPostSetInfoCleanup; + } + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> Getting file context for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p. FileContextCreated = %x)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + fileContext, + fileContextCreated) ); + + DebugTrace( DEBUG_TRACE_FILE_CONTEXT_OPERATIONS, + ("[Ctx]: CtxPostSetInfo -> File context info for file %wZ (Cbd = %p, FileObject = %p, FileContext = %p) \n\tName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + fileContext, + &fileContext->FileName) ); + + +CtxPostSetInfoCleanup: + + + // + // Release the references we have acquired + // + + if (instanceContext != NULL) { + + FltReleaseContext( instanceContext ); + } + + if (fileContext != NULL) { + + FltReleaseContext( fileContext ); + } + + if (streamContext != NULL) { + + FltReleaseContext( streamContext ); + } + + if (streamHandleContext != NULL) { + + FltReleaseContext( streamHandleContext ); + } + + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Ctx]: CtxPostSetInfo -> Failed with status 0x%x \n", + status) ); + + // + // It doesn't make sense to udate Cbd->IoStatus.Status on failure since the + // file system has suceesfully completed the operation + // + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[Ctx]: CtxPostSetInfo -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + diff --git a/filesys/miniFilter/ctx/pch.h b/filesys/miniFilter/ctx/pch.h new file mode 100644 index 00000000..7d2e3ef0 --- /dev/null +++ b/filesys/miniFilter/ctx/pch.h @@ -0,0 +1,46 @@ +/*++ + +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 __CTX_PCH_H__ +#define __CTX_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 <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> +#include "CtxStruc.h" +#include "CtxProc.h" + +#endif __CTX_PCH_H__ + diff --git a/filesys/miniFilter/ctx/support.c b/filesys/miniFilter/ctx/support.c new file mode 100644 index 00000000..7e3db95f --- /dev/null +++ b/filesys/miniFilter/ctx/support.c @@ -0,0 +1,117 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + operations.c + +Abstract: + + This is the support routines module of the kernel mode filter driver implementing + context sample. + + +Environment: + + Kernel mode + + +--*/ + + + +#include "pch.h" + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CtxAllocateUnicodeString) +#pragma alloc_text(PAGE, CtxFreeUnicodeString) +#endif + +// +// Support Routines +// + +_At_(String->Length, _Out_range_(==, 0)) +_At_(String->MaximumLength, _In_) +_At_(String->Buffer, _Pre_maybenull_ _Post_notnull_ _Post_writable_byte_size_(String->MaximumLength)) +NTSTATUS +CtxAllocateUnicodeString ( + _Out_ 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, + CTX_STRING_TAG ); + + if (String->Buffer == NULL) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[Ctx]: Failed to allocate unicode string of size 0x%x\n", + String->MaximumLength) ); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + String->Length = 0; + + return STATUS_SUCCESS; +} + +_At_(String->Length, _Out_range_(==, 0)) +_At_(String->MaximumLength, _Out_range_(==, 0)) +_At_(String->Buffer, _Pre_notnull_ _Post_null_) +VOID +CtxFreeUnicodeString ( + _Pre_notnull_ 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, + CTX_STRING_TAG ); + + String->Length = String->MaximumLength = 0; + String->Buffer = NULL; +} + + + diff --git a/filesys/miniFilter/delete/ReadMe.md b/filesys/miniFilter/delete/ReadMe.md new file mode 100644 index 00000000..50cfb0dd --- /dev/null +++ b/filesys/miniFilter/delete/ReadMe.md @@ -0,0 +1,16 @@ +Delete File System Minifilter Driver +==================================== + +The Delete minifilter is an example that demonstrates how to detect deletions of files or streams. Deletions are reported as debug output. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *delete* minifilter illustrates how to detect deletion of files and streams. It monitors IRP\_MJ\_CREATE requests for the FILE\_DELETE\_ON\_CLOSE flag. Also, it detects IRP\_MJ\_SET\_INFORMATION requests for setting FileDispositionInformation. The sample also illustrates how to handle racing deletes (in the form of multiple parallel IRP\_MJ\_SET\_INFORMATION operations), and how to distinguish deletion of an entire file from deletion of just one stream of the file. + +**Note** Because of the way in which the Windows operating system deletes files, it is not possible for the minifilter to detect in advance that a file or stream will be deleted. The minifilter can only detect operations that may cause a deletion, and then determine if the deletion took place after the operation completes. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. diff --git a/filesys/miniFilter/delete/delete.c b/filesys/miniFilter/delete/delete.c new file mode 100644 index 00000000..ec515c81 --- /dev/null +++ b/filesys/miniFilter/delete/delete.c @@ -0,0 +1,3253 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + delete.c + +Abstract: + + This is the main file for the delete detection sample minifilter. + + +Environment: + + Kernel mode + + +--*/ + + +#include <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + + + +#define DFDBG_TRACE_ERRORS 0x00000001 +#define DFDBG_TRACE_ROUTINES 0x00000002 +#define DFDBG_TRACE_OPERATION_STATUS 0x00000004 + +#define DF_VOLUME_GUID_NAME_SIZE 48 + +#define DF_INSTANCE_CONTEXT_POOL_TAG 'nIfD' +#define DF_STREAM_CONTEXT_POOL_TAG 'xSfD' +#define DF_TRANSACTION_CONTEXT_POOL_TAG 'xTfD' +#define DF_ERESOURCE_POOL_TAG 'sRfD' +#define DF_DELETE_NOTIFY_POOL_TAG 'nDfD' +#define DF_STRING_POOL_TAG 'rSfD' + +#define DF_CONTEXT_POOL_TYPE PagedPool + +#define DF_NOTIFICATION_MASK (TRANSACTION_NOTIFY_COMMIT_FINALIZE | \ + TRANSACTION_NOTIFY_ROLLBACK) + + +////////////////////////////////////////////////////////////////////////////// +// Macros // +////////////////////////////////////////////////////////////////////////////// + +#define DF_PRINT( ... ) \ + DbgPrintEx( DPFLTR_FLTMGR_ID, DPFLTR_ERROR_LEVEL, __VA_ARGS__ ) + +#define DF_DBG_PRINT( _dbgLevel, ... ) \ + (FlagOn( gTraceFlags, (_dbgLevel) ) ? \ + DF_PRINT( __VA_ARGS__ ): \ + (0)) + +#define FlagOnAll( F, T ) \ + (FlagOn( F, T ) == T) + + +////////////////////////////////////////////////////////////////////////////// +// Main Globals // +////////////////////////////////////////////////////////////////////////////// + +PFLT_FILTER gFilterHandle; +ULONG gTraceFlags = DFDBG_TRACE_ERRORS; + + +////////////////////////////////////////////////////////////////////////////// +// ReFS Compatibility Helpers // +////////////////////////////////////////////////////////////////////////////// + +// +// This helps us deal with ReFS 128-bit file IDs and NTFS 64-bit file IDs. +// + +typedef union _DF_FILE_REFERENCE { + + struct { + ULONGLONG Value; // The 64-bit file ID lives here. + ULONGLONG UpperZeroes; // In a 64-bit file ID this will be 0. + } FileId64; + + UCHAR FileId128[16]; // The 128-bit file ID lives here. + +} DF_FILE_REFERENCE, *PDF_FILE_REFERENCE; + +#define DfSizeofFileId(FID) ( \ + ((FID).FileId64.UpperZeroes == 0ll) ? \ + sizeof((FID).FileId64.Value) : \ + sizeof((FID).FileId128) \ + ) + + +////////////////////////////////////////////////////////////////////////////// +// Types // +////////////////////////////////////////////////////////////////////////////// + +// +// This is the instance context for this minifilter, it stores the volume's +// GUID name. +// + +typedef struct _DF_INSTANCE_CONTEXT { + + // + // Volume GUID name. + // + + UNICODE_STRING VolumeGuidName; + +} DF_INSTANCE_CONTEXT, *PDF_INSTANCE_CONTEXT; + + +// +// This is the stream context for this minifilter, attached whenever a stream +// becomes a candidate for deletion. +// + +typedef struct _DF_STREAM_CONTEXT { + + // + // FLT_FILE_NAME_INFORMATION structure with the names for this stream + // and file. This is only used for printing out the opened name when + // notifying deletes. This will be the result of an opened query name + // done at the last pre-cleanup on the file/stream. + // + // Therefore, there is no requirement of maintaining the file name + // information (for the purposes we use it) in sync with the FltMgr name + // cache or the file system. This makes it okay to store it in the stream + // context. + // + + PFLT_FILE_NAME_INFORMATION NameInfo; + + // + // File ID, obtained from querying the file system for FileInternalInformation. + // If the File ID is 128 bits (as in ReFS) we get it via FileIdInformation. + // + + DF_FILE_REFERENCE FileId; + + // + // Number of SetDisp operations in flight. + // + + volatile LONG NumOps; + + // + // IsNotified == 1 means a file/stream deletion was already notified. + // + + volatile LONG IsNotified; + + // + // Whether or not we've already queried the file ID. + // + + BOOLEAN FileIdSet; + + // + // Delete Disposition for this stream. + // + + BOOLEAN SetDisp; + + // + // Delete-on-Close state for this stream. + // + + BOOLEAN DeleteOnClose; + +} DF_STREAM_CONTEXT, *PDF_STREAM_CONTEXT; + + +// +// This is the transaction context for this minifilter, attached at post- +// -cleanup when notifying a delete within a transaction. +// + +typedef struct _DF_TRANSACTION_CONTEXT { + + // + // List of DF_DELETE_NOTIFY structures representing pending delete + // notifications. + // + + LIST_ENTRY DeleteNotifyList; + + // + // ERESOURCE for synchronized access to the DeleteNotifyList. + // + // ERESOURCEs must be allocated from NonPagedPool. If an ERESOURCE was + // declared here as a direct member of a structure, instead of just a + // pointer, then the whole transaction context would need to be allocated + // out of NonPagedPool. + // + // Therefore, declaring it as a pointer and only allocating at context + // initialization time helps us save some NonPagedPool. This is + // particularly important in larger context structures. + // + + PERESOURCE Resource; + +} DF_TRANSACTION_CONTEXT, *PDF_TRANSACTION_CONTEXT; + + +// +// This structure represents pending delete notifications for files that have +// been deleted in an open transaction. +// + +typedef struct _DF_DELETE_NOTIFY { + + // + // Links to other DF_DELETE_NOTIFY structures in the list. + // + + LIST_ENTRY Links; + + // + // Pointer to the stream context for the deleted stream/file. + // + + PDF_STREAM_CONTEXT StreamContext; + + // + // TRUE for a deleted file, FALSE for a stream. + // + + BOOLEAN FileDelete; + +} DF_DELETE_NOTIFY, *PDF_DELETE_NOTIFY; + + +////////////////////////////////////////////////////////////////////////////// +// Prototypes // +////////////////////////////////////////////////////////////////////////////// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +DfUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +DfInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +DfInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +DfInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +DfInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +DfSetupInstanceContext( + _In_ PCFLT_RELATED_OBJECTS FltObjects + ); + +NTSTATUS +DfAllocateContext ( + _In_ FLT_CONTEXT_TYPE ContextType, + _Outptr_ PFLT_CONTEXT *Context + ); + +NTSTATUS +DfSetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _In_ FLT_CONTEXT_TYPE ContextType, + _In_ PFLT_CONTEXT NewContext, + _Outptr_opt_result_maybenull_ PFLT_CONTEXT *OldContext + ); + +NTSTATUS +DfGetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _In_ FLT_CONTEXT_TYPE ContextType, + _Outptr_ PFLT_CONTEXT *Context + ); + +NTSTATUS +DfGetOrSetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _Outptr_ _Pre_valid_ PFLT_CONTEXT *Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +DfStreamContextCleanupCallback ( + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +DfTransactionContextCleanupCallback ( + _In_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +VOID +DfInstanceContextCleanupCallback ( + _In_ PDF_INSTANCE_CONTEXT InstanceContext, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +DfGetFileNameInformation ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PDF_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +DfAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +VOID +DfFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +NTSTATUS +DfBuildFileIdString ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext, + _Out_ PUNICODE_STRING String + ); + +NTSTATUS +DfDetectDeleteByFileId ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext + ); + +NTSTATUS +DfIsFileDeleted ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ BOOLEAN IsTransaction + ); + +NTSTATUS +DfAddTransDeleteNotify ( + _Inout_ PDF_STREAM_CONTEXT StreamContext, + _Inout_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ BOOLEAN FileDelete + ); + +VOID +DfNotifyDelete ( + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ BOOLEAN IsFile, + _Inout_opt_ PDF_TRANSACTION_CONTEXT TransactionContext + ); + +VOID +DfNotifyDeleteOnTransactionEnd ( + _In_ PDF_DELETE_NOTIFY DeleteNotify, + _In_ BOOLEAN Commit + ); + +NTSTATUS +DfProcessDelete ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext + ); + +FLT_PREOP_CALLBACK_STATUS +DfPreCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_result_maybenull_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +DfPostCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +DfPreSetInfoCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +DfPostSetInfoCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +DfPreCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +DfPostCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +NTSTATUS +DfTransactionNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG NotificationMask + ); + +NTSTATUS +DfGetVolumeGuidName ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PUNICODE_STRING VolumeGuidName + ); + +NTSTATUS +DfGetFileId ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PDF_STREAM_CONTEXT StreamContext + ); + +////////////////////////////////////////////////////////////////////////////// +// Text section assignments for all routines // +////////////////////////////////////////////////////////////////////////////// + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, DfUnload) +#pragma alloc_text(PAGE, DfInstanceSetup) +#pragma alloc_text(PAGE, DfInstanceQueryTeardown) +#pragma alloc_text(PAGE, DfInstanceTeardownStart) +#pragma alloc_text(PAGE, DfInstanceTeardownComplete) +#pragma alloc_text(PAGE, DfSetupInstanceContext) +#pragma alloc_text(PAGE, DfAllocateContext) +#pragma alloc_text(PAGE, DfSetContext) +#pragma alloc_text(PAGE, DfGetContext) +#pragma alloc_text(PAGE, DfGetOrSetContext) +#pragma alloc_text(PAGE, DfStreamContextCleanupCallback) +#pragma alloc_text(PAGE, DfTransactionContextCleanupCallback) +#pragma alloc_text(PAGE, DfInstanceContextCleanupCallback) +#pragma alloc_text(PAGE, DfGetFileNameInformation) +#pragma alloc_text(PAGE, DfAllocateUnicodeString) +#pragma alloc_text(PAGE, DfFreeUnicodeString) +#pragma alloc_text(PAGE, DfBuildFileIdString) +#pragma alloc_text(PAGE, DfDetectDeleteByFileId) +#pragma alloc_text(PAGE, DfIsFileDeleted) +#pragma alloc_text(PAGE, DfAddTransDeleteNotify) +#pragma alloc_text(PAGE, DfNotifyDelete) +#pragma alloc_text(PAGE, DfNotifyDeleteOnTransactionEnd) +#pragma alloc_text(PAGE, DfProcessDelete) +#pragma alloc_text(PAGE, DfPreCreateCallback) +#pragma alloc_text(PAGE, DfPostCreateCallback) +#pragma alloc_text(PAGE, DfPreSetInfoCallback) +#pragma alloc_text(PAGE, DfPostSetInfoCallback) +#pragma alloc_text(PAGE, DfPreCleanupCallback) +#pragma alloc_text(PAGE, DfPostCleanupCallback) +#pragma alloc_text(PAGE, DfTransactionNotificationCallback) +#pragma alloc_text(PAGE, DfGetVolumeGuidName) +#pragma alloc_text(PAGE, DfGetFileId) +#endif + + +////////////////////////////////////////////////////////////////////////////// +// Context Registration // +////////////////////////////////////////////////////////////////////////////// + +CONST FLT_CONTEXT_REGISTRATION Contexts[] = { + + { FLT_INSTANCE_CONTEXT, + 0, + DfInstanceContextCleanupCallback, + sizeof(DF_INSTANCE_CONTEXT), + DF_INSTANCE_CONTEXT_POOL_TAG, + NULL, + NULL, + NULL }, + + { FLT_STREAM_CONTEXT, + 0, + DfStreamContextCleanupCallback, + sizeof(DF_STREAM_CONTEXT), + DF_STREAM_CONTEXT_POOL_TAG, + NULL, + NULL, + NULL }, + + { FLT_TRANSACTION_CONTEXT, + 0, + DfTransactionContextCleanupCallback, + sizeof(DF_TRANSACTION_CONTEXT), + DF_TRANSACTION_CONTEXT_POOL_TAG, + NULL, + NULL, + NULL }, + + { FLT_CONTEXT_END } + +}; + + +////////////////////////////////////////////////////////////////////////////// +// Operation Registration // +////////////////////////////////////////////////////////////////////////////// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + 0, + DfPreCreateCallback, + DfPostCreateCallback }, + + { IRP_MJ_SET_INFORMATION, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + DfPreSetInfoCallback, + DfPostSetInfoCallback }, + + { IRP_MJ_CLEANUP, + 0, + DfPreCleanupCallback, + DfPostCleanupCallback }, + + { IRP_MJ_OPERATION_END } + +}; + + +////////////////////////////////////////////////////////////////////////////// +// Filter Registration // +////////////////////////////////////////////////////////////////////////////// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + Contexts, // Context + Callbacks, // Operation callbacks + + DfUnload, // MiniFilterUnload + + DfInstanceSetup, // InstanceSetup + DfInstanceQueryTeardown, // InstanceQueryTeardown + DfInstanceTeardownStart, // InstanceTeardownStart + DfInstanceTeardownComplete, // InstanceTeardownComplete + NULL, // GenerateFileName + NULL, // NormalizeNameComponent + NULL, // NormalizeContextCleanup + DfTransactionNotificationCallback, // TransactionNotification + NULL // NormalizeNameComponentEx + +}; + + +////////////////////////////////////////////////////////////////////////////// +// MiniFilter initialization and unload routines // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all 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; + + UNREFERENCED_PARAMETER( RegistryPath ); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DriverEntry: Entered\n" ); + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &gFilterHandle ); + + ASSERT( NT_SUCCESS( status ) ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering i/o + // + + status = FltStartFiltering( gFilterHandle ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( gFilterHandle ); + } + } + + return status; +} + + +NTSTATUS +DfUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter driver. This is called + when the minifilter is about to be unloaded. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfUnload: Entered\n" ); + + FltUnregisterFilter( gFilterHandle ); + + return STATUS_SUCCESS; +} + + +////////////////////////////////////////////////////////////////////////////// +// Filter Instance Callbacks (Setup/Teardown/QueryTeardown) // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DfInstanceSetup ( + _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. + + New instances are only created and attached to a volume if it is a writable + NTFS or ReFS volume. + +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. + + VolumeFilesystemType - A FLT_FSTYPE_* value indicating which file system type + the Filter Manager is offering to attach us to. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN isWritable = FALSE; + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfInstanceSetup: Entered\n" ); + + status = FltIsVolumeWritable( FltObjects->Volume, + &isWritable ); + + if (!NT_SUCCESS( status )) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Attaching to read-only volumes is pointless as you should not be able + // to delete files on such a volume. + // + + if (isWritable) { + + switch (VolumeFilesystemType) { + + case FLT_FSTYPE_NTFS: + case FLT_FSTYPE_REFS: + + status = STATUS_SUCCESS; + break; + + default: + + return STATUS_FLT_DO_NOT_ATTACH; + } + + } else { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + return status; +} + + +NTSTATUS +DfInstanceQueryTeardown ( + _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(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfInstanceQueryTeardown: Entered\n" ); + + return STATUS_SUCCESS; +} + + +VOID +DfInstanceTeardownStart ( + _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. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfInstanceTeardownStart: Entered\n" ); +} + + +VOID +DfInstanceTeardownComplete ( + _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 ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfInstanceTeardownComplete: Entered\n" ); +} + + +////////////////////////////////////////////////////////////////////////////// +// Context manipulation functions // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DfAllocateContext ( + _In_ FLT_CONTEXT_TYPE ContextType, + _Outptr_ PFLT_CONTEXT *Context + ) +/*++ + +Routine Description: + + This routine allocates and initializes a context of given type. + +Arguments: + + ContextType - Type of context to be allocated/initialized. + + Context - Pointer to a context pointer. + +Return Value: + + Returns a status forwarded from FltAllocateContext. + +--*/ +{ + NTSTATUS status; + PDF_TRANSACTION_CONTEXT transactionContext; + + PAGED_CODE(); + + switch (ContextType) { + + case FLT_STREAM_CONTEXT: + + status = FltAllocateContext( gFilterHandle, + FLT_STREAM_CONTEXT, + sizeof(DF_STREAM_CONTEXT), + DF_CONTEXT_POOL_TYPE, + Context ); + + if (NT_SUCCESS( status )) { + RtlZeroMemory( *Context, sizeof(DF_STREAM_CONTEXT) ); + } + + return status; + + case FLT_TRANSACTION_CONTEXT: + + status = FltAllocateContext( gFilterHandle, + FLT_TRANSACTION_CONTEXT, + sizeof(DF_TRANSACTION_CONTEXT), + DF_CONTEXT_POOL_TYPE, + Context ); + + if (NT_SUCCESS( status )) { + RtlZeroMemory( *Context, sizeof(DF_TRANSACTION_CONTEXT) ); + + transactionContext = *Context; + + InitializeListHead( &transactionContext->DeleteNotifyList ); + + transactionContext->Resource = ExAllocatePoolWithTag( NonPagedPool, + sizeof(ERESOURCE), + DF_ERESOURCE_POOL_TAG ); + + if (NULL == transactionContext->Resource) { + FltReleaseContext( transactionContext ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ExInitializeResourceLite( transactionContext->Resource ); + } + + return status; + + case FLT_INSTANCE_CONTEXT: + + status = FltAllocateContext( gFilterHandle, + FLT_INSTANCE_CONTEXT, + sizeof(DF_INSTANCE_CONTEXT), + DF_CONTEXT_POOL_TYPE, + Context ); + + if (NT_SUCCESS( status )) { + RtlZeroMemory( *Context, sizeof(DF_INSTANCE_CONTEXT) ); + } + + return status; + + default: + + return STATUS_INVALID_PARAMETER; + } +} + + +NTSTATUS +DfSetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _In_ FLT_CONTEXT_TYPE ContextType, + _In_ PFLT_CONTEXT NewContext, + _Outptr_opt_result_maybenull_ PFLT_CONTEXT *OldContext + ) +/*++ + +Routine Description: + + This routine sets the given context to the target. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Target - Pointer to the target to which we want to attach the + context. It will actually be either a FILE_OBJECT or + a KTRANSACTION. For instance contexts, it's ignored, as + the target is the FLT_INSTANCE itself, obtained from + Data->Iopb->TargetInstance. + + ContextType - Type of context to get/allocate/attach. Also used to + disambiguate the target/context type as this minifilter + only has one type of context per target. + + NewContext - Pointer to the context the caller wants to attach. + + OldContext - Returns the context already attached to the target, if + that is the case. + +Return Value: + + Returns a status forwarded from FltSetXxxContext. + +--*/ +{ + PAGED_CODE(); + + switch (ContextType) { + + case FLT_STREAM_CONTEXT: + + return FltSetStreamContext( FltObjects->Instance, + (PFILE_OBJECT)Target, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + NewContext, + OldContext ); + + case FLT_TRANSACTION_CONTEXT: + + return FltSetTransactionContext( FltObjects->Instance, + (PKTRANSACTION)Target, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + NewContext, + OldContext ); + + case FLT_INSTANCE_CONTEXT: + + return FltSetInstanceContext( FltObjects->Instance, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + NewContext, + OldContext ); + + default: + + ASSERT( !"Unexpected context type!\n" ); + + return STATUS_INVALID_PARAMETER; + } +} + + +NTSTATUS +DfGetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _In_ FLT_CONTEXT_TYPE ContextType, + _Outptr_ PFLT_CONTEXT *Context + ) +/*++ + +Routine Description: + + This routine gets the given context from the target. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Target - Pointer to the target from which we want to obtain the + context. It will actually be either a FILE_OBJECT or + a KTRANSACTION. For instance contexts, it's ignored, as + the target is the FLT_INSTANCE itself, obtained from + Data->Iopb->TargetInstance. + + ContextType - Type of context to get. Also used to disambiguate + the target/context type as this minifilter + only has one type of context per target. + + Context - Pointer returning a pointer to the attached context. + +Return Value: + + Returns a status forwarded from FltSetXxxContext. + +--*/ +{ + PAGED_CODE(); + + switch (ContextType) { + + case FLT_STREAM_CONTEXT: + + return FltGetStreamContext( FltObjects->Instance, + (PFILE_OBJECT)Target, + Context ); + + case FLT_TRANSACTION_CONTEXT: + + return FltGetTransactionContext( FltObjects->Instance, + (PKTRANSACTION)Target, + Context ); + + case FLT_INSTANCE_CONTEXT: + + return FltGetInstanceContext( FltObjects->Instance, + Context ); + + default: + + return STATUS_INVALID_PARAMETER; + } +} + + +NTSTATUS +DfGetOrSetContext ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target, + _Outptr_ _Pre_valid_ PFLT_CONTEXT *Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine obtains a context of type ContextType that is attached to + Target. + + If a context is already attached to Target, it will be returned in + *Context. If a context is already attached, but *Context points to + another context, *Context will be released. + + If no context is attached, and *Context points to a previously allocated + context, *Context will be attached to the Target. + + Finally, if no previously allocated context is passed to this routine + (*Context is a NULL pointer), a new Context is created and then attached + to Target. + + In case of race conditions (or the presence of a previously allocated + context at *Context), the existing attached context is returned via + *Context. + + In case of a transaction context, this function will also enlist in the + transaction. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance and its associated volume. + + Target - Pointer to the target to which we want to attach the + context. It will actually be either a FILE_OBJECT or + a KTRANSACTION. It is NULL for an Instance context. + + Context - Pointer to a pointer to a context. Used both for + returning an allocated/attached context or for receiving + a context to attach to the Target. + + ContextType - Type of context to get/allocate/attach. Also used to + disambiguate the target/context type as this minifilter + only has one type of context per target. + +Return Value: + + Returns a status forwarded from Flt(((Get|Set)Xxx)|Allocate)Context or + FltEnlistInTransaction. + +--*/ +{ + NTSTATUS status; + PFLT_CONTEXT newContext; + PFLT_CONTEXT oldContext; + + PAGED_CODE(); + + ASSERT( NULL != Context ); + + newContext = *Context; + + // + // Is there already a context attached to the target? + // + + status = DfGetContext( FltObjects, + Target, + ContextType, + &oldContext ); + + if (STATUS_NOT_FOUND == status) { + + // + // There is no attached context. This means we have to either attach the + // one provided by the caller or allocate a new one and attach it. + // + + if (NULL == newContext) { + + // + // No provided context. Allocate one. + // + + status = DfAllocateContext( ContextType, &newContext ); + + if (!NT_SUCCESS( status )) { + + // + // We failed to allocate. + // + + return status; + } + } + + } else if (!NT_SUCCESS( status )) { + + // + // We failed trying to get a context from the target. + // + + return status; + + } else { + + // + // There is already a context attached to the target, so return + // that context. + // + // If a context was provided by the caller, release it if it's not + // the one attached to the target. + // + + // + // The caller is not allowed to set the same context on the target + // twice. + // + ASSERT( newContext != oldContext ); + + if (NULL != newContext) { + + FltReleaseContext( newContext ); + } + + *Context = oldContext; + return status; + } + + // + // At this point we should have a context to set on the target (newContext). + // + + status = DfSetContext( FltObjects, + Target, + ContextType, + newContext, + &oldContext ); + + if (!NT_SUCCESS( status )) { + + // + // FltSetStreamContext failed so we must release the new context. + // + + FltReleaseContext( newContext ); + + if (STATUS_FLT_CONTEXT_ALREADY_DEFINED == status) { + + // + // We're racing with some other call which managed to set the + // context before us. We will return that context instead, which + // will be in oldContext. + // + + *Context = oldContext; + return STATUS_SUCCESS; + + } else { + + // + // Failed to set the context. Return NULL. + // + + *Context = NULL; + return status; + } + } + + // + // If this is setting a transaction context, we want to enlist in the + // transaction as well. + // + + if (FLT_TRANSACTION_CONTEXT == ContextType) { + + status = FltEnlistInTransaction( FltObjects->Instance, + (PKTRANSACTION)Target, + newContext, + DF_NOTIFICATION_MASK ); + + } + + // + // Setting the context was successful so just return newContext. + // + + *Context = newContext; + return status; +} + + +////////////////////////////////////////////////////////////////////////////// +// Context Cleanup Callbacks // +////////////////////////////////////////////////////////////////////////////// + +VOID +DfStreamContextCleanupCallback ( + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine cleans up a stream context. The only cleanup necessary is + releasing the FLT_FILE_NAME_INFORMATION object of the NameInfo field. + +Arguments: + + StreamContext - Pointer to DF_STREAM_CONTEXT to be cleaned up. + + ContextType - Type of StreamContext. Must be FLT_STREAM_CONTEXT. + +--*/ +{ + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + ASSERT( ContextType == FLT_STREAM_CONTEXT ); + + // + // Release NameInfo if present. + // + + if (StreamContext->NameInfo != NULL) { + + FltReleaseFileNameInformation(StreamContext->NameInfo); + StreamContext->NameInfo = NULL; + } +} + + +VOID +DfTransactionContextCleanupCallback ( + _In_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine cleans up a transaction context. + This operation consists basically of walking the DeleteNotifyList and + deleting all the deletion notifications pending on behalf of this + transaction. + +Arguments: + + TransactionContext - Pointer to DF_TRANSACTION_CONTEXT to be cleaned up. + + ContextType - Type of TransactionContext. Must be FLT_TRANSACTION_CONTEXT. + +--*/ +{ + PDF_DELETE_NOTIFY deleteNotify = NULL; + + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + ASSERT( ContextType == FLT_TRANSACTION_CONTEXT ); + + if (NULL != TransactionContext->Resource) { + + FltAcquireResourceExclusive( TransactionContext->Resource ); + + while (!IsListEmpty( &TransactionContext->DeleteNotifyList )) { + + // + // Remove every DF_DELETE_NOTIFY, releasing their corresponding + // FLT_FILE_NAME_INFORMATION objects and freeing pool used by + // them. + // + + deleteNotify = CONTAINING_RECORD( RemoveHeadList( &TransactionContext->DeleteNotifyList ), + DF_DELETE_NOTIFY, + Links ); + + FltReleaseContext( deleteNotify->StreamContext ); + ExFreePool( deleteNotify ); + + } + + FltReleaseResource( TransactionContext->Resource ); + + // + // Delete and free the DeleteNotifyList synchronization resource. + // + + ExDeleteResourceLite( TransactionContext->Resource ); + ExFreePool( TransactionContext->Resource ); + } +} + + +VOID +DfInstanceContextCleanupCallback ( + _In_ PDF_INSTANCE_CONTEXT InstanceContext, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + This routine cleans up an instance context, which consists on freeing + pool used by the volume GUID name string. + +Arguments: + + InstanceContext - Pointer to DF_INSTANCE_CONTEXT to be cleaned up. + + ContextType - Type of InstanceContext. Must be FLT_INSTANCE_CONTEXT. + +--*/ +{ + UNREFERENCED_PARAMETER( ContextType ); + + PAGED_CODE(); + + ASSERT( ContextType == FLT_INSTANCE_CONTEXT ); + + DfFreeUnicodeString( &InstanceContext->VolumeGuidName ); +} + + +////////////////////////////////////////////////////////////////////////////// +// Miscellaneous String, File Name and File ID Functions // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DfGetFileNameInformation ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PDF_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine gets and parses the file name information, obtains the File + ID and saves them in the stream context. + +Arguments: + + Data - Pointer to FLT_CALLBACK_DATA. + + StreamContext - Pointer to stream context that will receive the file + information. + +Return Value: + + Returns statuses forwarded from Flt(Get|Parse)FileNameInformation or + FltQueryInformationFile. + +--*/ +{ + NTSTATUS status; + PFLT_FILE_NAME_INFORMATION oldNameInfo; + PFLT_FILE_NAME_INFORMATION newNameInfo; + + PAGED_CODE(); + + // + // FltGetFileNameInformation - this is enough for a file name. + // + + status = FltGetFileNameInformation( Data, + (FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT), + &newNameInfo ); + + if (!NT_SUCCESS( status )) { + return status; + } + + // + // FltParseFileNameInformation - this fills in the other gaps, like the + // stream name, if present. + // + + status = FltParseFileNameInformation( newNameInfo ); + + if (!NT_SUCCESS( status )) { + return status; + } + + // + // Now that we have a good NameInfo, set it in the context, replacing + // the previous one. + // + + oldNameInfo = InterlockedExchangePointer( &StreamContext->NameInfo, + newNameInfo ); + + if (NULL != oldNameInfo) { + + FltReleaseFileNameInformation( oldNameInfo ); + } + + return status; +} + + +NTSTATUS +DfGetFileId ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PDF_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine obtains the File ID and saves it in the stream context. + +Arguments: + + Data - Pointer to FLT_CALLBACK_DATA. + + StreamContext - Pointer to stream context that will receive the file + ID. + +Return Value: + + Returns statuses forwarded from FltQueryInformationFile, including + STATUS_FILE_DELETED. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FILE_INTERNAL_INFORMATION fileInternalInformation; + + PAGED_CODE(); + + // + // Only query the file system for the file ID for the first time. + // This is just an optimization. It doesn't need any real synchronization + // because file IDs don't change. + // + + if (!StreamContext->FileIdSet) { + + // + // Querying for FileInternalInformation gives you the file ID. + // + + status = FltQueryInformationFile( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &fileInternalInformation, + sizeof(FILE_INTERNAL_INFORMATION), + FileInternalInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + // + // ReFS uses 128-bit file IDs. FileInternalInformation supports 64- + // bit file IDs. ReFS signals that a particular file ID can only + // be represented in 128 bits by returning FILE_INVALID_FILE_ID as + // the file ID. In that case we need to use FileIdInformation. + // + + if (fileInternalInformation.IndexNumber.QuadPart == FILE_INVALID_FILE_ID) { + + FILE_ID_INFORMATION fileIdInformation; + + status = FltQueryInformationFile( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &fileIdInformation, + sizeof(FILE_ID_INFORMATION), + FileIdInformation, + NULL ); + + if (NT_SUCCESS( status )) { + + // + // We don't use DfSizeofFileId() here because we are not + // measuring the size of a DF_FILE_REFERENCE. We know we have + // a 128-bit value. + // + + RtlCopyMemory( &StreamContext->FileId, + &fileIdInformation.FileId, + sizeof(StreamContext->FileId) ); + + // + // Because there's (currently) no support for 128-bit values in + // the compiler we need to ensure the setting of the ID and our + // remembering that the file ID was set occur in the right order. + // + + KeMemoryBarrier(); + + StreamContext->FileIdSet = TRUE; + } + + } else { + + StreamContext->FileId.FileId64.Value = fileInternalInformation.IndexNumber.QuadPart; + StreamContext->FileId.FileId64.UpperZeroes = 0ll; + + // + // Because there's (currently) no support for 128-bit values in + // the compiler we need to ensure the setting of the ID and our + // remembering that the file ID was set occur in the right order. + // + + KeMemoryBarrier(); + + StreamContext->FileIdSet = TRUE; + } + } + } + + return status; +} + + +NTSTATUS +DfAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This helper routine simply allocates a buffer for a UNICODE_STRING and + initializes its Length to zero. + + It uses whatever value is present in the MaximumLength field as the size + for the allocation. + +Arguments: + + String - Pointer to UNICODE_STRING. + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES if it was not possible to allocate the + buffer from pool. + + STATUS_SUCCESS otherwise. + +--*/ +{ + PAGED_CODE(); + + ASSERT( NULL != String ); + ASSERT( 0 != String->MaximumLength ); + + String->Length = 0; + + String->Buffer = ExAllocatePoolWithTag( DF_CONTEXT_POOL_TYPE, + String->MaximumLength, + DF_STRING_POOL_TAG ); + + if (NULL == String->Buffer) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + return STATUS_SUCCESS; +} + + +VOID +DfFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This helper routine frees the buffer of a UNICODE_STRING and resets its + Length to zero. + +Arguments: + + String - Pointer to UNICODE_STRING. + +--*/ +{ + PAGED_CODE(); + + ASSERT( NULL != String ); + ASSERT( 0 != String->MaximumLength ); + + String->Length = 0; + + if ( NULL != String->Buffer ) { + + String->MaximumLength = 0; + ExFreePool( String->Buffer ); + String->Buffer = NULL; + } +} + + +NTSTATUS +DfGetVolumeGuidName ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PUNICODE_STRING VolumeGuidName + ) +/*++ + +Routine Description: + + This helper routine returns a volume GUID name (with an added trailing + backslash for convenience) in the VolumeGuidName string passed by the + caller. + + The volume GUID name is cached in the instance context for the instance + attached to the volume, and this function will set up an instance context + with the cached name on it if there isn't one already attached to the + instance. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + VolumeGuidName - Pointer to UNICODE_STRING, returning the volume GUID name. + +Return Value: + + Return statuses forwarded by DfAllocateUnicodeString or + FltGetVolumeGuidName. On error, caller needs to DfFreeUnicodeString on + VolumeGuidName. + +--*/ +{ + NTSTATUS status; + PUNICODE_STRING sourceGuidName; + PDF_INSTANCE_CONTEXT instanceContext = NULL; + + PAGED_CODE(); + + // + // Obtain an instance context. Target is NULL for instance context, as + // the FLT_INSTANCE can be obtained from the FltObjects. + // + + status = DfGetOrSetContext( FltObjects, + NULL, + &instanceContext, + FLT_INSTANCE_CONTEXT ); + + if (NT_SUCCESS( status )) { + + // + // sourceGuidName is the source from where we'll copy the volume + // GUID name. Hopefully the name is present in the instance context + // already (buffer is not NULL) so we'll try to use that. + // + + sourceGuidName = &instanceContext->VolumeGuidName; + + if (NULL == sourceGuidName->Buffer) { + + // + // The volume GUID name is not cached in the instance context + // yet, so we will have to query the volume for it and put it + // in the instance context, so future queries can get it directly + // from the context. + // + + UNICODE_STRING tempString; + + // + // Add sizeof(WCHAR) so it's possible to add a trailing backslash here. + // + + tempString.MaximumLength = DF_VOLUME_GUID_NAME_SIZE * + sizeof(WCHAR) + + sizeof(WCHAR); + + status = DfAllocateUnicodeString( &tempString ); + + if (!NT_SUCCESS( status )) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!%s: DfAllocateUnicodeString returned 0x%08x!\n", + __FUNCTION__, + status ); + + return status; + } + + // while there is no guid name, don't do the open by id deletion logic. + // (it's actually better to defer obtaining the volume GUID name up to + // the point when we actually need it, in the open by ID scenario.) + status = FltGetVolumeGuidName( FltObjects->Volume, + &tempString, + NULL ); + + if (!NT_SUCCESS( status )) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!%s: FltGetVolumeGuidName returned 0x%08x!\n", + __FUNCTION__, + status ); + + DfFreeUnicodeString( &tempString ); + + return status; + } + + // + // Append trailing backslash. + // + + RtlAppendUnicodeToString( &tempString, L"\\" ); + + // + // Now set the sourceGuidName to the tempString. It is okay to + // set Length and MaximumLength with no synchronization because + // those will always be the same value (size of a volume GUID + // name with an extra trailing backslash). + // + + sourceGuidName->Length = tempString.Length; + sourceGuidName->MaximumLength = tempString.MaximumLength; + + // + // Setting the buffer, however, requires some synchronization, + // because another thread might be attempting to do the same, + // and even though they're exactly the same string, they're + // different allocations (buffers) so if the other thread we're + // racing with manages to set the buffer before us, we need to + // free our temporary string buffer. + // + + InterlockedCompareExchangePointer( &sourceGuidName->Buffer, + tempString.Buffer, + NULL ); + + if (sourceGuidName->Buffer != tempString.Buffer) { + + // + // We didn't manage to set the buffer, so let's free the + // tempString buffer. + // + + DfFreeUnicodeString( &tempString ); + } + } + + // + // sourceGuidName now contains the correct GUID name, so copy that + // to the caller string. + // + + RtlCopyUnicodeString( VolumeGuidName, sourceGuidName ); + + // + // We're done with the instance context. + // + + FltReleaseContext( instanceContext ); + } + + return status; +} + + +NTSTATUS +DfBuildFileIdString ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext, + _Out_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This helper routine builds a string used to open a file by its ID. + + It will assume the file ID is properly loaded in the stream context + (StreamContext->FileId). + +Arguments: + + Data - Pointer to FLT_CALLBACK_DATA. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - Pointer to the stream context. + + String - Pointer to UNICODE_STRING (output). + +Return Value: + + Return statuses forwarded by DfAllocateUnicodeString or + FltGetInstanceContext. + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + ASSERT( NULL != String ); + + // + // We'll compose the string with: + // 1. The volume GUID name. + // 2. A backslash + // 3. The File ID. + // + + // + // Make sure the file ID is loaded in the StreamContext. Note that if the + // file has been deleted DfGetFileId will return STATUS_FILE_DELETED. + // Since we're interested in detecting whether the file has been deleted + // that's fine; the open-by-ID will not actually take place. We have to + // ensure it is loaded before building the string length below since we + // may get either a 64-bit or 128-bit file ID back. + // + + status = DfGetFileId( Data, + StreamContext ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // First add the lengths of 1, 2, 3 and allocate accordingly. + // Note that ReFS understands both 64- and 128-bit file IDs when opening + // by ID, so whichever size we get back from DfSizeofFileId will work. + // + + String->MaximumLength = DF_VOLUME_GUID_NAME_SIZE * sizeof(WCHAR) + + sizeof(WCHAR) + + DfSizeofFileId( StreamContext->FileId ); + + status = DfAllocateUnicodeString( String ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Now obtain the volume GUID name with a trailing backslash (1 + 2). + // + + // obtain volume GUID name here and cache it in the InstanceContext. + status = DfGetVolumeGuidName( FltObjects, + String ); + + if (!NT_SUCCESS( status )) { + + DfFreeUnicodeString( String ); + + return status; + } + + // + // Now append the file ID to the end of the string. + // + + RtlCopyMemory( Add2Ptr( String->Buffer, String->Length ), + &StreamContext->FileId, + DfSizeofFileId( StreamContext->FileId )); + + String->Length += DfSizeofFileId( StreamContext->FileId ); + + ASSERT( String->Length == String->MaximumLength ); + + return status; +} + + +NTSTATUS +DfDetectDeleteByFileId ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This helper routine detects a deleted file by attempting to open it using + its file ID. + + If the file is successfully opened this routine closes the file before returning. + +Arguments: + + Data - Pointer to FLT_CALLBACK_DATA. + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + StreamContext - Pointer to the stream context. + +Return Value: + + STATUS_FILE_DELETED - Returned through DfBuildFileIdString if the file has + been deleted. + + STATUS_INVALID_PARAMETER - Returned from FltCreateFileEx2 when opening by ID + a file that doesn't exist. + + STATUS_DELETE_PENDING - The file has been set to be deleted when the last handle + goes away, but there are still open handles. + + Also any other NTSTATUS returned from DfBuildFileIdString, FltCreateFileEx2, + or FltClose. + +--*/ +{ + NTSTATUS status; + UNICODE_STRING fileIdString; + HANDLE handle; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK ioStatus; + IO_DRIVER_CREATE_CONTEXT driverCreateContext; + + PAGED_CODE(); + + // + // First build the file ID string. Note that this may fail with STATUS_FILE_DELETED + // and short-circuit our open-by-ID. Since we're really trying to see if + // the file is deleted, that's perfectly okay. + // + + status = DfBuildFileIdString( Data, + FltObjects, + StreamContext, + &fileIdString ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + InitializeObjectAttributes( &objectAttributes, + &fileIdString, + OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + // + // It is important to initialize the IO_DRIVER_CREATE_CONTEXT structure's + // TxnParameters. We'll always want to do this open on behalf of a + // transaction because opening the file by ID is the method we use to + // detect if the whole file still exists when we're in a transaction. + // + + IoInitializeDriverCreateContext( &driverCreateContext ); + driverCreateContext.TxnParameters = + IoGetTransactionParameterBlock( Data->Iopb->TargetFileObject ); + + status = FltCreateFileEx2( gFilterHandle, + Data->Iopb->TargetInstance, + &handle, + NULL, + FILE_READ_ATTRIBUTES, + &objectAttributes, + &ioStatus, + (PLARGE_INTEGER) NULL, + 0L, + FILE_SHARE_VALID_FLAGS, + FILE_OPEN, + FILE_OPEN_REPARSE_POINT | FILE_OPEN_BY_FILE_ID, + (PVOID) NULL, + 0L, + IO_IGNORE_SHARE_ACCESS_CHECK, + &driverCreateContext ); + + if (NT_SUCCESS( status )) { + + status = FltClose( handle ); + ASSERT( NT_SUCCESS( status ) ); + } + + DfFreeUnicodeString( &fileIdString ); + + return status; +} + + +////////////////////////////////////////////////////////////////////////////// +// Deletion Verification & Processing Functions // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DfIsFileDeleted ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ BOOLEAN IsTransaction + ) +/*++ + +Routine Description: + + This routine returns whether a file was deleted. It is called from + DfProcessDelete after an alternate data stream is deleted. This needs to + be done for the case when the last outstanding handle to a delete-pending + file is a handle to a delete-pending alternate data stream. When that + handle is closed, the whole file goes away, and we want to report a whole + file deletion, not just an alternate data stream deletion. + +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. + + StreamContext - Pointer to the stream context. + + IsTransaction - TRUE if in a transaction, FALSE otherwise. + +Return Value: + + STATUS_FILE_DELETED - The whole file was deleted. + Successful status - The file still exists, this was probably just a named + data stream being deleted. + Anything else - Failure in finding out if the file was deleted. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + FILE_OBJECTID_BUFFER fileObjectIdBuf; + + FLT_FILESYSTEM_TYPE fileSystemType; + + PAGED_CODE(); + + // + // We need to know whether we're on ReFS or NTFS. + // + + status = FltGetFileSystemType( FltObjects->Instance, + &fileSystemType ); + + if (status != STATUS_SUCCESS) { + + return status; + } + + // + // FSCTL_GET_OBJECT_ID does not return STATUS_FILE_DELETED if the + // file was deleted in a transaction, and this is why we need another + // method for detecting if the file is still present: opening by ID. + // + // If we're on ReFS we also need to open by file ID because ReFS does not + // support object IDs. + // + + if (IsTransaction || + (fileSystemType == FLT_FSTYPE_REFS)) { + + status = DfDetectDeleteByFileId( Data, + FltObjects, + StreamContext ); + + switch (status) { + + case STATUS_INVALID_PARAMETER: + + // + // The file was deleted. In this case, trying to open it + // by ID returns STATUS_INVALID_PARAMETER. + // + + return STATUS_FILE_DELETED; + + case STATUS_DELETE_PENDING: + + // + // In this case, the main file still exists, but is in + // a delete pending state, so we return STATUS_SUCCESS, + // signaling it still exists and wasn't deleted by this + // operation. + // + + return STATUS_SUCCESS; + + default: + + return status; + } + + } else { + + // + // When not in a transaction, attempting to get the object ID of the + // file is a cheaper alternative compared to opening the file by ID. + // + + status = FltFsControlFile( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + FSCTL_GET_OBJECT_ID, + NULL, + 0, + &fileObjectIdBuf, + sizeof(FILE_OBJECTID_BUFFER), + NULL ); + + switch (status) { + + case STATUS_OBJECTID_NOT_FOUND: + + // + // Getting back STATUS_OBJECTID_NOT_FOUND means the file + // still exists, it just doesn't have an object ID. + + return STATUS_SUCCESS; + + default: + + // + // Else we just get back STATUS_FILE_DELETED if the file + // doesn't exist anymore, or some error status, so no + // status conversion is necessary. + // + + NOTHING; + } + } + + return status; +} + + +NTSTATUS +DfAddTransDeleteNotify ( + _Inout_ PDF_STREAM_CONTEXT StreamContext, + _Inout_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ BOOLEAN FileDelete + ) +/*++ + +Routine Description: + + This routine adds a pending deletion notification (DF_DELETE_NOTIFY) + object to the transaction context DeleteNotifyList. It is called from + DfNotifyDelete when a file or stream gets deleted in a transaction. + +Arguments: + + StreamContext - Pointer to the stream context. + + TransactionContext - Pointer to the transaction context. + + FileDelete - TRUE if this is a FILE deletion, FALSE if it's a STREAM + deletion. + +Return Value: + + STATUS_SUCCESS. + +--*/ +{ + PDF_DELETE_NOTIFY deleteNotify; + + PAGED_CODE(); + + ASSERT( NULL != TransactionContext->Resource ); + + ASSERT( NULL != StreamContext ); + + deleteNotify = ExAllocatePoolWithTag( DF_CONTEXT_POOL_TYPE, + sizeof(DF_DELETE_NOTIFY), + DF_DELETE_NOTIFY_POOL_TAG ); + + if (NULL == deleteNotify) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory( deleteNotify, sizeof(DF_DELETE_NOTIFY) ); + + FltReferenceContext( StreamContext ); + deleteNotify->StreamContext = StreamContext; + deleteNotify->FileDelete = FileDelete; + + FltAcquireResourceExclusive( TransactionContext->Resource ); + + InsertTailList( &TransactionContext->DeleteNotifyList, + &deleteNotify->Links ); + + FltReleaseResource( TransactionContext->Resource ); + + return STATUS_SUCCESS; +} + + +VOID +DfNotifyDelete ( + _In_ PDF_STREAM_CONTEXT StreamContext, + _In_ BOOLEAN IsFile, + _Inout_opt_ PDF_TRANSACTION_CONTEXT TransactionContext + ) +/*++ + +Routine Description: + + This routine does the processing after it is verified, in the post-cleanup + callback, that a file or stream were deleted. It sorts out whether it's a + file or a stream delete, whether this is in a transacted context or not, + and issues the appropriate notifications. + +Arguments: + + StreamContext - Pointer to the stream context of the deleted file/stream. + + IsFile - TRUE if deleting a file, FALSE for an alternate data stream. + + TransactionContext - The transaction context. Present if in a transaction, + NULL otherwise. + +--*/ +{ + PAGED_CODE(); + + if (InterlockedIncrement( &StreamContext->IsNotified ) <= 1) { + + if (IsFile) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfPostCleanupCallback: " + "A file \"%wZ\" (%p) has been", + &StreamContext->NameInfo->Name, + StreamContext ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfPostCleanupCallback: " + "An alternate data stream \"%wZ\" (%p) has been", + &StreamContext->NameInfo->Name, + StreamContext ); + } + + // + // Flag that a delete has been notified on this file/stream. + // + + if (NULL == TransactionContext) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + " deleted!\n" ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + " deleted in a transaction!\n" ); + + DfAddTransDeleteNotify( StreamContext, + TransactionContext, + IsFile ); + } + } +} + + +VOID +DfNotifyDeleteOnTransactionEnd ( + _In_ PDF_DELETE_NOTIFY DeleteNotify, + _In_ BOOLEAN Commit + ) +/*++ + +Routine Description: + + This routine is called by the transaction notification callback to issue + the proper notifications for a file that has been deleted in the context + of that transaction. + The file will be reported as finally deleted, if the transaction was + committed, or "saved" if the transaction was rolled back. + +Arguments: + + DeleteNotify - Pointer to the DF_DELETE_NOTIFY object that contains the + data necessary for issuing this notification. + + Commit - TRUE if the transaction was committed, FALSE if it was + rolled back. + +--*/ +{ + PAGED_CODE(); + + if (DeleteNotify->FileDelete) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfTransactionNotificationCallback: " + "A file \"%wZ\" (%p) has been", + &DeleteNotify->StreamContext->NameInfo->Name, + DeleteNotify->StreamContext ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfTransactionNotificationCallback: " + "An alternate data stream \"%wZ\" (%p) has been", + &DeleteNotify->StreamContext->NameInfo->Name, + DeleteNotify->StreamContext ); + } + + if (Commit) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + " deleted due to a transaction commit!\n" ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + " saved due to a transaction rollback!\n" ); + } +} + + +NTSTATUS +DfProcessDelete ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_STREAM_CONTEXT StreamContext + ) +/*++ + +Routine Description: + + This routine does the processing after it is verified, in the post-cleanup + callback, that a file or stream were deleted. It sorts out whether it's a + file or a stream delete, whether this is in a transacted context or not, + and issues the appropriate notifications. + +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. + + StreamContext - Pointer to the stream context of the deleted file/stream. + +Return Value: + + STATUS_SUCCESS. + +--*/ +{ + BOOLEAN isTransaction; + BOOLEAN isFileDeleted = FALSE; + NTSTATUS status; + PDF_TRANSACTION_CONTEXT transactionContext = NULL; + + PAGED_CODE(); + + // Is this in a transacted context? + isTransaction = (NULL != FltObjects->Transaction); + + if (isTransaction) { + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfProcessDelete: In a transaction!\n" ); + + status = DfGetOrSetContext( FltObjects, + FltObjects->Transaction, + &transactionContext, + FLT_TRANSACTION_CONTEXT ); + + if (!NT_SUCCESS( status )) { + + return status; + } + } + + // + // Notify deletion. If this is an Alternate Data Stream being deleted, + // check if the whole file was deleted (by calling DfIsFileDeleted) as + // this could be the last handle to a delete-pending file. + // + + status = DfIsFileDeleted( Data, + FltObjects, + StreamContext, + isTransaction ); + + if (STATUS_FILE_DELETED == status) { + + isFileDeleted = TRUE; + status = STATUS_SUCCESS; + + } else if (!NT_SUCCESS( status )) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!%s: DfIsFileDeleted returned 0x%08x!\n", + __FUNCTION__, + status ); + + goto _exit; + } + + DfNotifyDelete( StreamContext, + isFileDeleted, + transactionContext ); + +_exit: + + if (NULL != transactionContext) { + + FltReleaseContext( transactionContext ); + } + + return status; +} + + +////////////////////////////////////////////////////////////////////////////// +// MiniFilter Operation Callback Routines // +////////////////////////////////////////////////////////////////////////////// + +FLT_PREOP_CALLBACK_STATUS +DfPreCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Outptr_result_maybenull_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-operation completion routine for + IRP_MJ_CREATE in this miniFilter. + + In the pre-create phase we're concerned with creates with + FILE_DELETE_ON_CLOSE set, and in those cases we want to flag + this stream as a candidate for being deleted. + +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: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - When FILE_DELETE_ON_CLOSE is set and + a stream context is created. + + FLT_PREOP_SUCCESS_NO_CALLBACK - When FILE_DELETE_ON_CLOSE is not set + and no stream context is created. + +--*/ +{ + PDF_STREAM_CONTEXT streamContext; + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfPreCreateCallback: Entered\n" ); + + // + // Creates are only interesting in the FILE_DELETE_ON_CLOSE scenario, + // in which we'll want to flag this file as a candidate for being + // deleted. + // + // The way we do that is allocate a stream context for this and return + // FLT_PREOP_SUCCESS_NO_CALLBACK, passing down the stream context via + // the completion context, so that the post-create callback can, in case + // of a successful create, attach this context to the stream and flag it + // as a real deletion candidate. + // + + if (FlagOn( Data->Iopb->Parameters.Create.Options, FILE_DELETE_ON_CLOSE )) { + + status = DfAllocateContext( FLT_STREAM_CONTEXT, + &streamContext ); + + if (NT_SUCCESS( status )) { + + *CompletionContext = (PVOID)streamContext; + + return FLT_PREOP_SYNCHRONIZE; + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfPreCreateCallback: An error occurred with DfAllocateStreamContext!\n" ); + } + } + + *CompletionContext = NULL; + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_POSTOP_CALLBACK_STATUS +DfPostCreateCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-operation completion routine for + IRP_MJ_CREATE in this miniFilter. + + The post-create callback will only be called when this is a create with + FILE_DELETE_ON_CLOSE, meaning we have to flag it as a deletion candidate. + +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. This will point to a DF_STREAM_CONTEXT allocated by + DfPreCreateCallback, which will be used for flagging this stream + as a deletion candidate. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous + processing here. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDF_STREAM_CONTEXT streamContext = NULL; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + ASSERT( NULL != CompletionContext ); + + streamContext = (PDF_STREAM_CONTEXT)CompletionContext; + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfPostCreateCallback: Entered\n" ); + + // this status check handles the draining scenario. + if (NT_SUCCESS( Data->IoStatus.Status ) && + (STATUS_REPARSE != Data->IoStatus.Status)) { + + // assert we're not draining. + ASSERT( !FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) ); + + // + // Flag the stream as a deletion candidate: try setting the stream + // context on it to the stream context allocated by DfPreCreateCallback. + // If a context is already attached to the stream, DfGetOrSetContext + // will do the right thing and set streamContext to it, freeing the + // other context. + // + + status = DfGetOrSetContext( FltObjects, + Data->Iopb->TargetFileObject, + &streamContext, + FLT_STREAM_CONTEXT ); + + if (NT_SUCCESS( status )) { + + // + // Set DeleteOnClose on the stream context: a delete-on-close stream will + // always be checked for deletion on cleanup. + // + + streamContext->DeleteOnClose = BooleanFlagOn( Data->Iopb->Parameters.Create.Options, + FILE_DELETE_ON_CLOSE ); + } + } + + // + // We will have a context in streamContext, because if allocation fails + // in DfPreCreateCallback, FLT_PREOP_SUCCESS_NO_CALLBACK is returned, so + // there is no post-create callback. + // + // If DfGetOrSetContext failed, if will have released streamContext + // already, so only release it if status is successful. + // + + if (NT_SUCCESS( status )) { + + FltReleaseContext( streamContext ); + } + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +DfPreSetInfoCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-operation completion routine for + IRP_MJ_SET_INFORMATION in this miniFilter. + + The pre-setinfo callback is important because setting + FileDispositionInformation is another way of putting the file in a + delete-pending state. + + Since the delete disposition is a reversible condition, we have to + make sure to do the right thing when multiple operations are racing: + we won't be able to tell the the final outcome of the delete + disposition state of the stream, so everytime a race like that happens, + we assume this stream as a permanent deletion candidate, so it will be + checked for deletion in the post-cleanup callback. + +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: + + FLT_PREOP_SYNCHRONIZE - we never do any sort of asynchronous processing + here, and we synchronize postop. + + FLT_PREOP_SUCCESS_NO_CALLBACK - if not FileDispositionInformation or we + can't set a streamcontext. + +--*/ +{ + NTSTATUS status; + PDF_STREAM_CONTEXT streamContext = NULL; + BOOLEAN race; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass) { + + case FileDispositionInformation: + + // + // We're interested when the file delete disposition changes. + // + + status = DfGetOrSetContext( FltObjects, + Data->Iopb->TargetFileObject, + &streamContext, + FLT_STREAM_CONTEXT ); + + if (!NT_SUCCESS( status )) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Race detection logic. The NumOps field in the StreamContext + // counts the number of in-flight changes to delete disposition + // on the stream. + // + // If there's already some operations in flight, don't bother + // doing postop. Since there will be no postop, this value won't + // be decremented, staying forever 2 or more, which is one of + // the conditions for checking deletion at post-cleanup. + // + + race = (InterlockedIncrement( &streamContext->NumOps ) > 1); + + if (!race) { + + // + // This is the only operation in flight, so do a postop on + // it because the final outcome of the delete disposition + // state of the stream is deterministic. + // + + *CompletionContext = (PVOID)streamContext; + + return FLT_PREOP_SYNCHRONIZE; + + } else { + + FltReleaseContext( streamContext ); + } + + // FALL_THROUGH + + default: + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + + break; + } +} + + +FLT_POSTOP_CALLBACK_STATUS +DfPostSetInfoCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-operation completion routine for + IRP_MJ_SET_INFORMATION in this miniFilter. + + In this postop callback we will update the deletion disposition state + of this stream in the stream context. This callback will only be reached + when there's a single change to deletion disposition in flight for the + stream or when this was the first of many racing ops to hit the preop. + + In the latter case, the race is already detected and adequately flagged + in the other preops, so we're safe just decrementing NumOps, because the + other operations will never reach postop and NumOps won't ever be + decremented for them, guaranteeing that NumOps will stay nonzero forever, + effectively flagging the race. + +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: + + FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous + processing here. + +--*/ +{ + PDF_STREAM_CONTEXT streamContext; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + // assert on FileDispositionInformation + ASSERT( Data->Iopb->Parameters.SetFileInformation.FileInformationClass + == FileDispositionInformation ); + + // pass from pre-callback to post-callback + ASSERT( NULL != CompletionContext ); + streamContext = (PDF_STREAM_CONTEXT) CompletionContext; + + // + // Reaching a postop for FileDispositionInformation means we + // MUST have a stream context passed in the CompletionContext. + // + + if (NT_SUCCESS( Data->IoStatus.Status )) { + + // + // No synchronization is needed to set the SetDisp field, + // because in case of races, the NumOps field will be perpetually + // positive, and it being positive is already an indication this + // file is a delete candidate, so it will be checked at post- + // -cleanup regardless of the value of SetDisp. + // + + streamContext->SetDisp = ((PFILE_DISPOSITION_INFORMATION) + Data->Iopb->Parameters.SetFileInformation.InfoBuffer)->DeleteFile; + } + + // + // Now that the operation is over, decrement NumOps. + // + + InterlockedDecrement( &streamContext->NumOps ); + + FltReleaseContext( streamContext ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +DfPreCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the pre-operation completion routine for + IRP_MJ_CLEANUP in this miniFilter. + + In the preop callback for cleanup, we obtain the file information and + save it in the stream context, just so we have a name to use when + reporting file deletions. + + That is done for every stream with an attached stream context because + those will be deletion candidates most of the time. + +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: + + FLT_PREOP_SYNCHRONIZE - we never do any sort of asynchronous processing + here, and we want to synchronize the postop. + + FLT_PREOP_SUCCESS_NO_CALLBACK - when we don't manage to get a stream + context. + +--*/ +{ + PDF_STREAM_CONTEXT streamContext; + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfPreCleanupCallback: Entered\n" ); + + status = FltGetStreamContext( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &streamContext ); + + if (NT_SUCCESS( status )) { + + // + // Only streams with stream context will be sent for deletion check + // in post-cleanup, which makes sense because they would only ever + // have one if they were flagged as candidates at some point. + // + // Gather file information here so that we have a name to report. + // The name will be accurate most of the times, and in the cases it + // won't, it serves as a good clue and the stream context pointer + // value should offer a way to disambiguate that in case of renames + // etc. + // + + status = DfGetFileNameInformation( Data, streamContext ); + + if (NT_SUCCESS( status )) { + + // pass from pre-callback to post-callback + *CompletionContext = (PVOID)streamContext; + + return FLT_PREOP_SYNCHRONIZE; + + } else { + + FltReleaseContext( streamContext ); + } + } + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_POSTOP_CALLBACK_STATUS +DfPostCleanupCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-operation completion routine for + IRP_MJ_CLEANUP in this miniFilter. + + Post-cleanup is the core of this minifilter. Here we check to see if + the stream or file were deleted and report that through DbgPrint. + +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 completion context set in the pre-operation routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous + processing here. + +--*/ +{ + FILE_STANDARD_INFORMATION fileInfo; + PDF_STREAM_CONTEXT streamContext = NULL; + NTSTATUS status; + + UNREFERENCED_PARAMETER( CompletionContext ); + + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DF_DBG_PRINT( DFDBG_TRACE_ROUTINES, + "delete!DfPostCleanupCallback: Entered\n" ); + + // assert we're not draining. + ASSERT( !FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) ); + + // pass from pre-callback to post-callback + ASSERT( NULL != CompletionContext ); + streamContext = (PDF_STREAM_CONTEXT) CompletionContext; + + if (NT_SUCCESS( Data->IoStatus.Status )) { + + // + // Determine whether or not we should check for deletion. What + // flags a file as a deletion candidate is one or more of the following: + // + // 1. NumOps > 0. This means there are or were racing changes to + // the file delete disposition state, and, in that case, + // we don't know what that state is. So, let's err to the side of + // caution and check if it was deleted. + // + // 2. SetDisp. If this is TRUE and we haven't raced in setting delete + // disposition, this reflects the true delete disposition state of the + // file, meaning we must check for deletes if it is set to TRUE. + // + // 3. DeleteOnClose. If the file was ever opened with + // FILE_DELETE_ON_CLOSE, we must check to see if it was deleted. + // + // Also, if a deletion of this stream was already notified, there is no + // point notifying it again. + // + + if (((streamContext->NumOps > 0) || + (streamContext->SetDisp) || + (streamContext->DeleteOnClose)) && + (0 == streamContext->IsNotified)) { + + // + // The check for deletion is done via a query to + // FileStandardInformation. If that returns STATUS_FILE_DELETED + // it means the stream was deleted. + // + + status = FltQueryInformationFile( Data->Iopb->TargetInstance, + Data->Iopb->TargetFileObject, + &fileInfo, + sizeof(fileInfo), + FileStandardInformation, + NULL ); + + if (STATUS_FILE_DELETED == status) { + + status = DfProcessDelete( Data, + FltObjects, + streamContext ); + + if (!NT_SUCCESS( status )) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!%s: It was not possible to verify " + "deletion due to an error in DfProcessDelete (0x%08x)!\n", + __FUNCTION__, + status ); + } + } + } + } + + FltReleaseContext( streamContext ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +NTSTATUS +DfTransactionNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PDF_TRANSACTION_CONTEXT TransactionContext, + _In_ ULONG NotificationMask + ) +/*++ + +Routine Description: + + This routine is the transaction notification callback for this minifilter. + It is called when a transaction we're enlisted in is committed or rolled + back so that it's possible to emit notifications about files that were + deleted in that transaction. + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + TransactionContext - The transaction context, set/modified when a delete + is detected. + + NotificationMask - A mask of flags indicating the notifications received + from FltMgr. Should be either TRANSACTION_NOTIFY_COMMIT or + TRANSACTION_NOTIFY_ROLLBACK. + +Return Value: + + STATUS_SUCCESS - This operation is never pended. + +--*/ +{ + BOOLEAN commit = BooleanFlagOn( NotificationMask, TRANSACTION_NOTIFY_COMMIT_FINALIZE ); + PDF_DELETE_NOTIFY deleteNotify = NULL; + + UNREFERENCED_PARAMETER( FltObjects ); + + PAGED_CODE(); + + // + // There is no such thing as a simultaneous commit and rollback, nor + // should we get notifications for events other than a commit or a + // rollback. + // + + ASSERT( (!FlagOnAll( NotificationMask, (DF_NOTIFICATION_MASK) )) && + FlagOn( NotificationMask, (DF_NOTIFICATION_MASK) ) ); + + if (commit) { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfTransactionNotificationCallback: COMMIT!\n" ); + + } else { + + DF_DBG_PRINT( DFDBG_TRACE_ERRORS, + "delete!DfTransactionNotificationCallback: ROLLBACK!\n" ); + } + + ASSERT( NULL != TransactionContext->Resource ); + + FltAcquireResourceExclusive( TransactionContext->Resource ); + + while (!IsListEmpty( &TransactionContext->DeleteNotifyList )) { + + deleteNotify = CONTAINING_RECORD( RemoveHeadList( &TransactionContext->DeleteNotifyList ), + DF_DELETE_NOTIFY, + Links ); + + ASSERT( NULL != deleteNotify->StreamContext ); + + if (!commit) { + InterlockedDecrement( &deleteNotify->StreamContext->IsNotified ); + } + + DfNotifyDeleteOnTransactionEnd( deleteNotify, + commit ); + + // release stream context + FltReleaseContext( deleteNotify->StreamContext ); + ExFreePool( deleteNotify ); + } + + FltReleaseResource( TransactionContext->Resource ); + + return STATUS_SUCCESS; +} + + + diff --git a/filesys/miniFilter/delete/delete.inf b/filesys/miniFilter/delete/delete.inf new file mode 100644 index 00000000..350f6df7 --- /dev/null +++ b/filesys/miniFilter/delete/delete.inf @@ -0,0 +1,96 @@ +;;; +;;; delete +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = delete.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +delete.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Delete Notification Mini-Filter Driver" +ServiceName = "delete" +DriverName = "delete" +DiskId1 = "delete Device Installation Disk" + +;Instances specific information. +DefaultInstance = "delete Instance" +Instance1.Name = "delete Instance" +Instance1.Altitude = "370150" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/delete/delete.rc b/filesys/miniFilter/delete/delete.rc new file mode 100644 index 00000000..fdffe048 --- /dev/null +++ b/filesys/miniFilter/delete/delete.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Delete Notification Filter Driver" +#define VER_INTERNALNAME_STR "delete.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/delete/delete.sln b/filesys/miniFilter/delete/delete.sln new file mode 100644 index 00000000..3af04bb5 --- /dev/null +++ b/filesys/miniFilter/delete/delete.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "delete", "delete.vcxproj", "{A872AE22-1A64-4531-A85F-03361BE73894}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|Win32.ActiveCfg = Debug|Win32 + {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|Win32.Build.0 = Debug|Win32 + {A872AE22-1A64-4531-A85F-03361BE73894}.Release|Win32.ActiveCfg = Release|Win32 + {A872AE22-1A64-4531-A85F-03361BE73894}.Release|Win32.Build.0 = Release|Win32 + {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|x64.ActiveCfg = Debug|x64 + {A872AE22-1A64-4531-A85F-03361BE73894}.Debug|x64.Build.0 = Debug|x64 + {A872AE22-1A64-4531-A85F-03361BE73894}.Release|x64.ActiveCfg = Release|x64 + {A872AE22-1A64-4531-A85F-03361BE73894}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/delete/delete.vcxproj b/filesys/miniFilter/delete/delete.vcxproj new file mode 100644 index 00000000..c2863872 --- /dev/null +++ b/filesys/miniFilter/delete/delete.vcxproj @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{A872AE22-1A64-4531-A85F-03361BE73894}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{DDF5DA16-05BC-4E87-9743-B11BFFA79395}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>delete</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>delete</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>delete</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>delete</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="delete.c" /> + <ResourceCompile Include="delete.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/delete/delete.vcxproj.Filters b/filesys/miniFilter/delete/delete.vcxproj.Filters new file mode 100644 index 00000000..994af6a4 --- /dev/null +++ b/filesys/miniFilter/delete/delete.vcxproj.Filters @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{0433160E-D11D-43E0-962A-F91BE56E9913}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{1BDAC8A0-7CBD-4CC1-9BC9-531A473E2C93}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{391C5CBD-0397-4FCB-AFAE-CBDE3F118A7F}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{1791F586-ED65-4309-8E44-3A9A74B4E2D7}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="delete.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="delete.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/minispy/ReadMe.md b/filesys/miniFilter/minispy/ReadMe.md new file mode 100644 index 00000000..12c114d2 --- /dev/null +++ b/filesys/miniFilter/minispy/ReadMe.md @@ -0,0 +1,17 @@ +Minispy File System Minifilter Driver +===================================== + +The Minispy sample is a tool to monitor and log any I/O and transaction activity that occurs in the system. Minispy is implemented as a minifilter. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +Minispy consists of both user-mode and kernel-mode components. The kernel-mode component registers callback functions that correspond to various I/O and transaction operations with the filter manager. These callback functions help Minispy record any I/O and transaction activity occurring in the system. When a user can request the recorded information, the recorded information is passed to the user-mode component, which can either output it on screen or log it to a file on disk. + +To observe I/O activity on a device, you must explicitly attach Minispy to that device by using the Minispy user-mode component. Similarly, you can request Minispy to stop logging data for a particular device. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/minispy/filter/RegistrationData.c b/filesys/miniFilter/minispy/filter/RegistrationData.c new file mode 100644 index 00000000..52d42ec6 --- /dev/null +++ b/filesys/miniFilter/minispy/filter/RegistrationData.c @@ -0,0 +1,302 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + RegistrationData.c + +Abstract: + + This filters registration information. Note that this is in a unique file + so it could be set into the INIT section. + +Environment: + + Kernel mode + +--*/ + +#include "mspyKern.h" + +//--------------------------------------------------------------------------- +// Registration information for FLTMGR. +//--------------------------------------------------------------------------- + +// +// Tells the compiler to define all following DATA and CONSTANT DATA to +// be placed in the INIT segment. +// + +#ifdef ALLOC_DATA_PRAGMA + #pragma data_seg("INIT") + #pragma const_seg("INIT") +#endif + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_CREATE_NAMED_PIPE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_CLOSE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_READ, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_WRITE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_INFORMATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_INFORMATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_EA, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_EA, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_FLUSH_BUFFERS, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_VOLUME_INFORMATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_VOLUME_INFORMATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_DIRECTORY_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_DEVICE_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_INTERNAL_DEVICE_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SHUTDOWN, + 0, + SpyPreOperationCallback, + NULL }, //post operation callback not supported + + { IRP_MJ_LOCK_CONTROL, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_CLEANUP, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_CREATE_MAILSLOT, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_SECURITY, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_SECURITY, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_QUERY_QUOTA, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_SET_QUOTA, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_PNP, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_ACQUIRE_FOR_MOD_WRITE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_RELEASE_FOR_MOD_WRITE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_ACQUIRE_FOR_CC_FLUSH, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_RELEASE_FOR_CC_FLUSH, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + +/* { IRP_MJ_NOTIFY_STREAM_FILE_OBJECT, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback },*/ + + { IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_MDL_READ, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_MDL_READ_COMPLETE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_PREPARE_MDL_WRITE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_MDL_WRITE_COMPLETE, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_VOLUME_MOUNT, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_VOLUME_DISMOUNT, + 0, + SpyPreOperationCallback, + SpyPostOperationCallback }, + + { IRP_MJ_OPERATION_END } +}; + +const FLT_CONTEXT_REGISTRATION Contexts[] = { + +#if MINISPY_VISTA + + { FLT_TRANSACTION_CONTEXT, + 0, + SpyDeleteTxfContext, + sizeof(MINISPY_TRANSACTION_CONTEXT), + 'ypsM' }, + +#endif // MINISPY_VISTA + + { FLT_CONTEXT_END } +}; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof(FLT_REGISTRATION), // Size + FLT_REGISTRATION_VERSION, // Version +#if MINISPY_WIN8 + FLTFL_REGISTRATION_SUPPORT_NPFS_MSFS, // Flags +#else + 0, // Flags +#endif // MINISPY_WIN8 + + Contexts, // Context + Callbacks, // Operation callbacks + + SpyFilterUnload, // FilterUnload + + NULL, // InstanceSetup + SpyQueryTeardown, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent + +#if MINISPY_VISTA + + , + SpyKtmNotificationCallback // KTM notification callback + +#endif // MINISPY_VISTA + +}; + + +// +// Tells the compiler to restore the given section types back to their previous +// section definition. +// + +#ifdef ALLOC_DATA_PRAGMA + #pragma data_seg() + #pragma const_seg() +#endif + diff --git a/filesys/miniFilter/minispy/filter/minispy.c b/filesys/miniFilter/minispy/filter/minispy.c new file mode 100644 index 00000000..5e4c59cf --- /dev/null +++ b/filesys/miniFilter/minispy/filter/minispy.c @@ -0,0 +1,1380 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + MiniSpy.c + +Abstract: + + This is the main module for the MiniSpy mini-filter. + +Environment: + + Kernel mode + +--*/ + +#include "mspyKern.h" +#include <stdio.h> + +// +// Global variables +// + +MINISPY_DATA MiniSpyData; +NTSTATUS StatusToBreakOn = 0; + +//--------------------------------------------------------------------------- +// Function prototypes +//--------------------------------------------------------------------------- +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + + +NTSTATUS +SpyMessage ( + _In_ PVOID ConnectionCookie, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ ULONG InputBufferSize, + _Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferSize, + _Out_ PULONG ReturnOutputBufferLength + ); + +NTSTATUS +SpyConnect( + _In_ PFLT_PORT ClientPort, + _In_ PVOID ServerPortCookie, + _In_reads_bytes_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Flt_ConnectionCookie_Outptr_ PVOID *ConnectionCookie + ); + +VOID +SpyDisconnect( + _In_opt_ PVOID ConnectionCookie + ); + +NTSTATUS +SpyEnlistInTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects + ); + +//--------------------------------------------------------------------------- +// Assign text sections for each routine. +//--------------------------------------------------------------------------- + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, DriverEntry) + #pragma alloc_text(PAGE, SpyFilterUnload) + #pragma alloc_text(PAGE, SpyQueryTeardown) + #pragma alloc_text(PAGE, SpyConnect) + #pragma alloc_text(PAGE, SpyDisconnect) + #pragma alloc_text(PAGE, SpyMessage) +#endif + + +#define SetFlagInterlocked(_ptrFlags,_flagToSet) \ + ((VOID)InterlockedOr(((volatile LONG *)(_ptrFlags)),_flagToSet)) + +//--------------------------------------------------------------------------- +// ROUTINES +//--------------------------------------------------------------------------- + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine is called when a driver first loads. Its purpose is to + initialize global state and then register with FltMgr to start filtering. + +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: + + Status of the operation. + +--*/ +{ + PSECURITY_DESCRIPTOR sd; + OBJECT_ATTRIBUTES oa; + UNICODE_STRING uniString; + NTSTATUS status = STATUS_SUCCESS; + + try { + + // + // Initialize global data structures. + // + + MiniSpyData.LogSequenceNumber = 0; + MiniSpyData.MaxRecordsToAllocate = DEFAULT_MAX_RECORDS_TO_ALLOCATE; + MiniSpyData.RecordsAllocated = 0; + MiniSpyData.NameQueryMethod = DEFAULT_NAME_QUERY_METHOD; + + MiniSpyData.DriverObject = DriverObject; + + InitializeListHead( &MiniSpyData.OutputBufferList ); + KeInitializeSpinLock( &MiniSpyData.OutputBufferLock ); + + ExInitializeNPagedLookasideList( &MiniSpyData.FreeBufferList, + NULL, + NULL, + POOL_NX_ALLOCATION, + RECORD_SIZE, + SPY_TAG, + 0 ); + +#if MINISPY_VISTA + + // + // Dynamically import FilterMgr APIs for transaction support + // + +#pragma warning(push) +#pragma warning(disable:4055) // type cast from data pointer to function pointer + MiniSpyData.PFltSetTransactionContext = (PFLT_SET_TRANSACTION_CONTEXT) FltGetRoutineAddress( "FltSetTransactionContext" ); + MiniSpyData.PFltGetTransactionContext = (PFLT_GET_TRANSACTION_CONTEXT) FltGetRoutineAddress( "FltGetTransactionContext" ); + MiniSpyData.PFltEnlistInTransaction = (PFLT_ENLIST_IN_TRANSACTION) FltGetRoutineAddress( "FltEnlistInTransaction" ); +#pragma warning(pop) + +#endif + + // + // Read the custom parameters for MiniSpy from the registry + // + + SpyReadDriverParameters(RegistryPath); + + // + // Now that our global configuration is complete, register with FltMgr. + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &MiniSpyData.Filter ); + + if (!NT_SUCCESS( status )) { + + leave; + } + + + status = FltBuildDefaultSecurityDescriptor( &sd, + FLT_PORT_ALL_ACCESS ); + + if (!NT_SUCCESS( status )) { + leave; + } + + RtlInitUnicodeString( &uniString, MINISPY_PORT_NAME ); + + InitializeObjectAttributes( &oa, + &uniString, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, + NULL, + sd ); + + status = FltCreateCommunicationPort( MiniSpyData.Filter, + &MiniSpyData.ServerPort, + &oa, + NULL, + SpyConnect, + SpyDisconnect, + SpyMessage, + 1 ); + + FltFreeSecurityDescriptor( sd ); + + if (!NT_SUCCESS( status )) { + leave; + } + + // + // We are now ready to start filtering + // + + status = FltStartFiltering( MiniSpyData.Filter ); + + } finally { + + if (!NT_SUCCESS( status ) ) { + + if (NULL != MiniSpyData.ServerPort) { + FltCloseCommunicationPort( MiniSpyData.ServerPort ); + } + + if (NULL != MiniSpyData.Filter) { + FltUnregisterFilter( MiniSpyData.Filter ); + } + + ExDeleteNPagedLookasideList( &MiniSpyData.FreeBufferList ); + } + } + + return status; +} + +NTSTATUS +SpyConnect( + _In_ PFLT_PORT ClientPort, + _In_ PVOID ServerPortCookie, + _In_reads_bytes_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Flt_ConnectionCookie_Outptr_ PVOID *ConnectionCookie + ) +/*++ + +Routine Description + + This is called when user-mode connects to the server + port - to establish a connection + +Arguments + + ClientPort - This is the pointer to the client port that + will be used to send messages from the filter. + ServerPortCookie - unused + ConnectionContext - unused + SizeofContext - unused + ConnectionCookie - unused + +Return Value + + STATUS_SUCCESS - to accept the connection +--*/ +{ + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ServerPortCookie ); + UNREFERENCED_PARAMETER( ConnectionContext ); + UNREFERENCED_PARAMETER( SizeOfContext); + UNREFERENCED_PARAMETER( ConnectionCookie ); + + FLT_ASSERT( MiniSpyData.ClientPort == NULL ); + MiniSpyData.ClientPort = ClientPort; + return STATUS_SUCCESS; +} + + +VOID +SpyDisconnect( + _In_opt_ PVOID ConnectionCookie + ) +/*++ + +Routine Description + + This is called when the connection is torn-down. We use it to close our handle to the connection + +Arguments + + ConnectionCookie - unused + +Return value + + None +--*/ +{ + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ConnectionCookie ); + + // + // Close our handle + // + + FltCloseClientPort( MiniSpyData.Filter, &MiniSpyData.ClientPort ); +} + +NTSTATUS +SpyFilterUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is called when a request has been made to unload the filter. Unload + requests from the Operation System (ex: "sc stop minispy" can not be + failed. Other unload requests may be failed. + + You can disallow OS unload request by setting the + FLTREGFL_DO_NOT_SUPPORT_SERVICE_STOP flag in the FLT_REGISTARTION + structure. + +Arguments: + + Flags - Flags pertinent to this operation + +Return Value: + + Always success + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + // + // Close the server port. This will stop new connections. + // + + FltCloseCommunicationPort( MiniSpyData.ServerPort ); + + FltUnregisterFilter( MiniSpyData.Filter ); + + SpyEmptyOutputBufferList(); + ExDeleteNPagedLookasideList( &MiniSpyData.FreeBufferList ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +SpyQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This allows our filter to be manually detached from a volume. + +Arguments: + + FltObjects - Contains pointer to relevant objects for this operation. + Note that the FileObject field will always be NULL. + + Flags - Flags pertinent to this operation + +Return Value: + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + PAGED_CODE(); + return STATUS_SUCCESS; +} + + +NTSTATUS +SpyMessage ( + _In_ PVOID ConnectionCookie, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ ULONG InputBufferSize, + _Out_writes_bytes_to_opt_(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer, + _In_ ULONG OutputBufferSize, + _Out_ PULONG ReturnOutputBufferLength + ) +/*++ + +Routine Description: + + This is called whenever a user mode application wishes to communicate + with this minifilter. + +Arguments: + + ConnectionCookie - unused + + OperationCode - An identifier describing what type of message this + is. These codes are defined by the MiniFilter. + InputBuffer - A buffer containing input data, can be NULL if there + is no input data. + InputBufferSize - The size in bytes of the InputBuffer. + OutputBuffer - A buffer provided by the application that originated + the communication in which to store data to be returned to this + application. + OutputBufferSize - The size in bytes of the OutputBuffer. + ReturnOutputBufferSize - The size in bytes of meaningful data + returned in the OutputBuffer. + +Return Value: + + Returns the status of processing the message. + +--*/ +{ + MINISPY_COMMAND command; + NTSTATUS status; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ConnectionCookie ); + + // + // **** PLEASE READ **** + // + // The INPUT and OUTPUT buffers are raw user mode addresses. The filter + // manager has already done a ProbedForRead (on InputBuffer) and + // ProbedForWrite (on OutputBuffer) which guarentees they are valid + // addresses based on the access (user mode vs. kernel mode). The + // minifilter does not need to do their own probe. + // + // The filter manager is NOT doing any alignment checking on the pointers. + // The minifilter must do this themselves if they care (see below). + // + // The minifilter MUST continue to use a try/except around any access to + // these buffers. + // + + if ((InputBuffer != NULL) && + (InputBufferSize >= (FIELD_OFFSET(COMMAND_MESSAGE,Command) + + sizeof(MINISPY_COMMAND)))) { + + try { + + // + // Probe and capture input message: the message is raw user mode + // buffer, so need to protect with exception handler + // + + command = ((PCOMMAND_MESSAGE) InputBuffer)->Command; + + } except (SpyExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + switch (command) { + + case GetMiniSpyLog: + + // + // Return as many log records as can fit into the OutputBuffer + // + + if ((OutputBuffer == NULL) || (OutputBufferSize == 0)) { + + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // We want to validate that the given buffer is POINTER + // aligned. But if this is a 64bit system and we want to + // support 32bit applications we need to be careful with how + // we do the check. Note that the way SpyGetLog is written + // it actually does not care about alignment but we are + // demonstrating how to do this type of check. + // + +#if defined(_WIN64) + + if (IoIs32bitProcess( NULL )) { + + // + // Validate alignment for the 32bit process on a 64bit + // system + // + + if (!IS_ALIGNED(OutputBuffer,sizeof(ULONG))) { + + status = STATUS_DATATYPE_MISALIGNMENT; + break; + } + + } else { + +#endif + + if (!IS_ALIGNED(OutputBuffer,sizeof(PVOID))) { + + status = STATUS_DATATYPE_MISALIGNMENT; + break; + } + +#if defined(_WIN64) + + } + +#endif + + // + // Get the log record. + // + + status = SpyGetLog( OutputBuffer, + OutputBufferSize, + ReturnOutputBufferLength ); + break; + + + case GetMiniSpyVersion: + + // + // Return version of the MiniSpy filter driver. Verify + // we have a valid user buffer including valid + // alignment + // + + if ((OutputBufferSize < sizeof( MINISPYVER )) || + (OutputBuffer == NULL)) { + + status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Validate Buffer alignment. If a minifilter cares about + // the alignment value of the buffer pointer they must do + // this check themselves. Note that a try/except will not + // capture alignment faults. + // + + if (!IS_ALIGNED(OutputBuffer,sizeof(ULONG))) { + + status = STATUS_DATATYPE_MISALIGNMENT; + break; + } + + // + // Protect access to raw user-mode output buffer with an + // exception handler + // + + try { + + ((PMINISPYVER)OutputBuffer)->Major = MINISPY_MAJ_VERSION; + ((PMINISPYVER)OutputBuffer)->Minor = MINISPY_MIN_VERSION; + + } except (SpyExceptionFilter( GetExceptionInformation(), TRUE )) { + + return GetExceptionCode(); + } + + *ReturnOutputBufferLength = sizeof( MINISPYVER ); + status = STATUS_SUCCESS; + break; + + default: + status = STATUS_INVALID_PARAMETER; + break; + } + + } else { + + status = STATUS_INVALID_PARAMETER; + } + + return status; +} + + +//--------------------------------------------------------------------------- +// Operation filtering routines +//--------------------------------------------------------------------------- + + +FLT_PREOP_CALLBACK_STATUS +#pragma warning(suppress: 6262) // higher than usual stack usage is considered safe in this case +SpyPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine receives ALL pre-operation callbacks for this filter. It then + tries to log information about the given operation. If we are able + to log information then we will call our post-operation callback routine. + + NOTE: This routine must be NON-PAGED because it can be called on the + paging path. + +Arguments: + + Data - Contains information about the given operation. + + FltObjects - Contains pointers to the various objects that are pertinent + to this operation. + + CompletionContext - This receives the address of our log buffer for this + operation. Our completion routine then receives this buffer address. + +Return Value: + + Identifies how processing should continue for this operation + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call our completion routine + PRECORD_LIST recordList; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + UNICODE_STRING defaultName; + PUNICODE_STRING nameToUse; + NTSTATUS status; + +#if MINISPY_VISTA + + PUNICODE_STRING ecpDataToUse = NULL; + UNICODE_STRING ecpData; + WCHAR ecpDataBuffer[MAX_NAME_SPACE/sizeof(WCHAR)]; + +#endif + +#if MINISPY_NOT_W2K + + WCHAR name[MAX_NAME_SPACE/sizeof(WCHAR)]; + +#endif + + // + // Try and get a log record + // + + recordList = SpyNewRecord(); + + if (recordList) { + + // + // We got a log record, if there is a file object, get its name. + // + // NOTE: By default, we use the query method + // FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP + // because MiniSpy would like to get the name as much as possible, but + // can cope if we can't retrieve a name. For a debugging type filter, + // like Minispy, this is reasonable, but for most production filters + // who need names reliably, they should query the name at times when it + // is known to be safe and use the query method + // FLT_FILE_NAME_QUERY_DEFAULT. + // + + if (FltObjects->FileObject != NULL) { + + status = FltGetFileNameInformation( Data, + FLT_FILE_NAME_NORMALIZED | + MiniSpyData.NameQueryMethod, + &nameInfo ); + + } else { + + // + // Can't get a name when there's no file object + // + + status = STATUS_UNSUCCESSFUL; + } + + // + // Use the name if we got it else use a default name + // + + if (NT_SUCCESS( status )) { + + nameToUse = &nameInfo->Name; + + // + // Parse the name if requested + // + + if (FlagOn( MiniSpyData.DebugFlags, SPY_DEBUG_PARSE_NAMES )) { + +#ifdef DBG + + FLT_ASSERT( NT_SUCCESS( FltParseFileNameInformation( nameInfo ) ) ); + +#else + + FltParseFileNameInformation( nameInfo ); + +#endif + + } + + } else { + +#if MINISPY_NOT_W2K + + NTSTATUS lstatus; + PFLT_FILE_NAME_INFORMATION lnameInfo; + + // + // If we couldn't get the "normalized" name try and get the + // "opened" name + // + + if (FltObjects->FileObject != NULL) { + + // + // Get the opened name + // + + lstatus = FltGetFileNameInformation( Data, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, + &lnameInfo ); + + + if (NT_SUCCESS(lstatus)) { + +#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + (VOID)_snwprintf( name, + sizeof(name)/sizeof(WCHAR), + L"<%08x> %wZ", + status, + &lnameInfo->Name ); + + FltReleaseFileNameInformation( lnameInfo ); + + } else { + + // + // If that failed report both NORMALIZED status and + // OPENED status + // + +#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + (VOID)_snwprintf( name, + sizeof(name)/sizeof(WCHAR), + L"<NO NAME: NormalizeStatus=%08x OpenedStatus=%08x>", + status, + lstatus ); + } + + } else { + +#pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + (VOID)_snwprintf( name, + sizeof(name)/sizeof(WCHAR), + L"<NO NAME>" ); + + } + + // + // Name was initialized by _snwprintf() so it may not be null terminated + // if the buffer is insufficient. We will ignore this error and truncate + // the file name. + // + + name[(sizeof(name)/sizeof(WCHAR))-1] = L'\0'; + + RtlInitUnicodeString( &defaultName, name ); + nameToUse = &defaultName; + +#else + + // + // We were unable to get the String safe routine to work on W2K + // Do it the old safe way + // + + RtlInitUnicodeString( &defaultName, L"<NO NAME>" ); + nameToUse = &defaultName; + +#endif //MINISPY_NOT_W2K + +#if DBG + + // + // Debug support to break on certain errors. + // + + if (FltObjects->FileObject != NULL) { + NTSTATUS retryStatus; + + if ((StatusToBreakOn != 0) && (status == StatusToBreakOn)) { + + DbgBreakPoint(); + } + + retryStatus = FltGetFileNameInformation( Data, + FLT_FILE_NAME_NORMALIZED | + MiniSpyData.NameQueryMethod, + &nameInfo ); + + if (!NT_SUCCESS( retryStatus )) { + + // + // We always release nameInfo, so ignore return value. + // + + NOTHING; + } + } + +#endif + + } + +#if MINISPY_VISTA + + // + // Look for ECPs, but only if it's a create operation + // + + if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) { + + // + // Initialize an empty string to receive an ECP data dump + // + + RtlInitEmptyUnicodeString( &ecpData, + ecpDataBuffer, + MAX_NAME_SPACE/sizeof(WCHAR) ); + + // + // Parse any extra create parameters + // + + SpyParseEcps( Data, recordList, &ecpData ); + + ecpDataToUse = &ecpData; + } + + // + // Store the name and ECP data (if any) + // + + SpySetRecordNameAndEcpData( &(recordList->LogRecord), nameToUse, ecpDataToUse ); + +#else + + // + // Store the name + // + + SpySetRecordName( &(recordList->LogRecord), nameToUse ); + +#endif + + // + // Release the name information structure (if defined) + // + + if (NULL != nameInfo) { + + FltReleaseFileNameInformation( nameInfo ); + } + + // + // Set all of the operation information into the record + // + + SpyLogPreOperationData( Data, FltObjects, recordList ); + + // + // Pass the record to our completions routine and return that + // we want our completion routine called. + // + + if (Data->Iopb->MajorFunction == IRP_MJ_SHUTDOWN) { + + // + // Since completion callbacks are not supported for + // this operation, do the completion processing now + // + + SpyPostOperationCallback( Data, + FltObjects, + recordList, + 0 ); + + returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + + } else { + + *CompletionContext = recordList; + returnStatus = FLT_PREOP_SUCCESS_WITH_CALLBACK; + } + } + + return returnStatus; +} + + +FLT_POSTOP_CALLBACK_STATUS +SpyPostOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine receives ALL post-operation callbacks. This will take + the log record passed in the context parameter and update it with + the completion information. It will then insert it on a list to be + sent to the usermode component. + + NOTE: This routine must be NON-PAGED because it can be called at DPC level + +Arguments: + + Data - Contains information about the given operation. + + FltObjects - Contains pointers to the various objects that are pertinent + to this operation. + + CompletionContext - Pointer to the RECORD_LIST structure in which we + store the information we are logging. This was passed from the + pre-operation callback + + Flags - Contains information as to why this routine was called. + +Return Value: + + Identifies how processing should continue for this operation + +--*/ +{ + PRECORD_LIST recordList; + PRECORD_LIST reparseRecordList = NULL; + PLOG_RECORD reparseLogRecord; + PFLT_TAG_DATA_BUFFER tagData; + ULONG copyLength; + + UNREFERENCED_PARAMETER( FltObjects ); + + recordList = (PRECORD_LIST)CompletionContext; + + // + // If our instance is in the process of being torn down don't bother to + // log this record, free it now. + // + + if (FlagOn(Flags,FLTFL_POST_OPERATION_DRAINING)) { + + SpyFreeRecord( recordList ); + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Set completion information into the record + // + + SpyLogPostOperationData( Data, recordList ); + + // + // Log reparse tag information if specified. + // + + tagData = Data->TagData; + if (tagData) { + + reparseRecordList = SpyNewRecord(); + + if (reparseRecordList) { + + // + // only copy the DATA portion of the information + // + + RtlCopyMemory( &reparseRecordList->LogRecord.Data, + &recordList->LogRecord.Data, + sizeof(RECORD_DATA) ); + + reparseLogRecord = &reparseRecordList->LogRecord; + + copyLength = FLT_TAG_DATA_BUFFER_HEADER_SIZE + tagData->TagDataLength; + + if(copyLength > MAX_NAME_SPACE) { + + copyLength = MAX_NAME_SPACE; + } + + // + // Copy reparse data + // + + RtlCopyMemory( + &reparseRecordList->LogRecord.Name[0], + tagData, + copyLength + ); + + reparseLogRecord->RecordType |= RECORD_TYPE_FILETAG; + reparseLogRecord->Length += (ULONG) ROUND_TO_SIZE( copyLength, sizeof( PVOID ) ); + } + } + + // + // Send the logged information to the user service. + // + + SpyLog( recordList ); + + if (reparseRecordList) { + + SpyLog( reparseRecordList ); + } + + // + // For creates within a transaction enlist in the transaction + // if we haven't already done. + // + + if ((FltObjects->Transaction != NULL) && + (Data->Iopb->MajorFunction == IRP_MJ_CREATE) && + (Data->IoStatus.Status == STATUS_SUCCESS)) { + + // + // Enlist in the transaction. + // + + SpyEnlistInTransaction( FltObjects ); + } + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +NTSTATUS +SpyEnlistInTransaction ( + _In_ PCFLT_RELATED_OBJECTS FltObjects + ) +/*++ + +Routine Description + + Minispy calls this function to enlist in a transaction of interest. + +Arguments + + FltObjects - Contains parameters required to enlist in a transaction. + +Return value + + Returns STATUS_SUCCESS if we were able to successfully enlist in a new transcation or if we + were already enlisted in the transaction. Returns an appropriate error code on a failure. + +--*/ +{ + +#if MINISPY_VISTA + + PMINISPY_TRANSACTION_CONTEXT transactionContext = NULL; + PMINISPY_TRANSACTION_CONTEXT oldTransactionContext = NULL; + PRECORD_LIST recordList; + NTSTATUS status; + static ULONG Sequence=1; + + // + // This code is only built in the Vista environment, but + // we need to ensure this binary still runs down-level. Return + // at this point if the transaction dynamic imports were not found. + // + // If we find FltGetTransactionContext, we assume the other + // transaction APIs are also present. + // + + if (NULL == MiniSpyData.PFltGetTransactionContext) { + + return STATUS_SUCCESS; + } + + // + // Try to get our context for this transaction. If we get + // one we have already enlisted in this transaction. + // + + status = (*MiniSpyData.PFltGetTransactionContext)( FltObjects->Instance, + FltObjects->Transaction, + &transactionContext ); + + if (NT_SUCCESS( status )) { + + // + // Check if we have already enlisted in the transaction. + // + + if (FlagOn(transactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION)) { + + // + // FltGetTransactionContext puts a reference on the context. Release + // that now and return success. + // + + FltReleaseContext( transactionContext ); + return STATUS_SUCCESS; + } + + // + // If we have not enlisted then we need to try and enlist in the transaction. + // + + goto ENLIST_IN_TRANSACTION; + } + + // + // If the context does not exist create a new one, else return the error + // status to the caller. + // + + if (status != STATUS_NOT_FOUND) { + + return status; + } + + // + // Allocate a transaction context. + // + + status = FltAllocateContext( FltObjects->Filter, + FLT_TRANSACTION_CONTEXT, + sizeof(MINISPY_TRANSACTION_CONTEXT), + PagedPool, + &transactionContext ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Set the context into the transaction + // + + RtlZeroMemory(transactionContext, sizeof(MINISPY_TRANSACTION_CONTEXT)); + transactionContext->Count = Sequence++; + + FLT_ASSERT( MiniSpyData.PFltSetTransactionContext ); + + status = (*MiniSpyData.PFltSetTransactionContext)( FltObjects->Instance, + FltObjects->Transaction, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + transactionContext, + &oldTransactionContext ); + + if (!NT_SUCCESS( status )) { + + FltReleaseContext( transactionContext ); //this will free the context + + if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + return status; + } + + FLT_ASSERT(oldTransactionContext != NULL); + + if (FlagOn(oldTransactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION)) { + + // + // If this context is already enlisted then release the reference + // which FltSetTransactionContext put on it and return success. + // + + FltReleaseContext( oldTransactionContext ); + return STATUS_SUCCESS; + } + + // + // If we found an existing transaction then we should try and + // enlist in it. There is a race here in which the thread + // which actually set the transaction context may fail to + // enlist in the transaction and delete it later. It might so + // happen that we picked up a reference to that context here + // and successfully enlisted in that transaction. For now + // we have chosen to ignore this scenario. + // + + // + // If we are not enlisted then assign the right transactionContext + // and attempt enlistment. + // + + transactionContext = oldTransactionContext; + } + +ENLIST_IN_TRANSACTION: + + // + // Enlist on this transaction for notifications. + // + + FLT_ASSERT( MiniSpyData.PFltEnlistInTransaction ); + + status = (*MiniSpyData.PFltEnlistInTransaction)( FltObjects->Instance, + FltObjects->Transaction, + transactionContext, + FLT_MAX_TRANSACTION_NOTIFICATIONS ); + + // + // If the enlistment failed we might have to delete the context and remove + // our count. + // + + if (!NT_SUCCESS( status )) { + + // + // If the error is that we are already enlisted then we do not need + // to delete the context. Otherwise we have to delete the context + // before releasing our reference. + // + + if (status == STATUS_FLT_ALREADY_ENLISTED) { + + status = STATUS_SUCCESS; + + } else { + + // + // It is worth noting that only the first caller of + // FltDeleteContext will remove the reference added by + // filter manager when the context was set. + // + + FltDeleteContext( transactionContext ); + } + + FltReleaseContext( transactionContext ); + return status; + } + + // + // Set the flag so that future enlistment efforts know that we + // successfully enlisted in the transaction. + // + + SetFlagInterlocked( &transactionContext->Flags, MINISPY_ENLISTED_IN_TRANSACTION ); + + // + // The operation succeeded, remove our count + // + + FltReleaseContext( transactionContext ); + + // + // Log a record that a new transaction has started. + // + + recordList = SpyNewRecord(); + + if (recordList) { + + SpyLogTransactionNotify( FltObjects, recordList, 0 ); + + // + // Send the logged information to the user service. + // + + SpyLog( recordList ); + } + +#endif // MINISPY_VISTA + + return STATUS_SUCCESS; +} + + +#if MINISPY_VISTA + +NTSTATUS +SpyKtmNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ) +{ + PRECORD_LIST recordList; + + UNREFERENCED_PARAMETER( TransactionContext ); + + // + // Try and get a log record + // + + recordList = SpyNewRecord(); + + if (recordList) { + + SpyLogTransactionNotify( FltObjects, recordList, TransactionNotification ); + + // + // Send the logged information to the user service. + // + + SpyLog( recordList ); + } + + return STATUS_SUCCESS; +} + +#endif // MINISPY_VISTA + +VOID +SpyDeleteTxfContext ( + _Inout_ PMINISPY_TRANSACTION_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +{ + UNREFERENCED_PARAMETER( Context ); + UNREFERENCED_PARAMETER( ContextType ); + + FLT_ASSERT(FLT_TRANSACTION_CONTEXT == ContextType); + FLT_ASSERT(Context->Count != 0); +} + + +LONG +SpyExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ) +/*++ + +Routine Description: + + Exception filter to catch errors touching user buffers. + +Arguments: + + ExceptionPointer - The exception record. + + AccessingUserBuffer - If TRUE, overrides FsRtlIsNtStatusExpected to allow + the caller to munge the error to a desired status. + +Return Value: + + EXCEPTION_EXECUTE_HANDLER - If the exception handler should be run. + + EXCEPTION_CONTINUE_SEARCH - If a higher exception handler should take care of + this exception. + +--*/ +{ + NTSTATUS Status; + + Status = ExceptionPointer->ExceptionRecord->ExceptionCode; + + // + // Certain exceptions shouldn't be dismissed within the namechanger filter + // unless we're touching user memory. + // + + if (!FsRtlIsNtstatusExpected( Status ) && + !AccessingUserBuffer) { + + return EXCEPTION_CONTINUE_SEARCH; + } + + return EXCEPTION_EXECUTE_HANDLER; +} + + diff --git a/filesys/miniFilter/minispy/filter/minispy.rc b/filesys/miniFilter/minispy/filter/minispy.rc new file mode 100644 index 00000000..b303a14b --- /dev/null +++ b/filesys/miniFilter/minispy/filter/minispy.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "MiniSpy Filter Driver" +#define VER_INTERNALNAME_STR "minispy.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/minispy/filter/minispy.vcxproj b/filesys/miniFilter/minispy/filter/minispy.vcxproj new file mode 100644 index 00000000..de6dac81 --- /dev/null +++ b/filesys/miniFilter/minispy/filter/minispy.vcxproj @@ -0,0 +1,198 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{99B46F3E-1CC2-4689-8D3E-80CCBD448E39}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{88181562-E49D-4E01-B470-89CECAA14E2E}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>minispy</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>minispy</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>minispy</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>minispy</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalOptions>%(AdditionalOptions) /map</AdditionalOptions> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalOptions>%(AdditionalOptions) /map</AdditionalOptions> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalOptions>%(AdditionalOptions) /map</AdditionalOptions> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalOptions>%(AdditionalOptions) /map</AdditionalOptions> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE</PreprocessorDefinitions> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="minispy.c" /> + <ClCompile Include="mspyLib.c" /> + <ClCompile Include="RegistrationData.c" /> + <ResourceCompile Include="minispy.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters b/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters new file mode 100644 index 00000000..7425e30c --- /dev/null +++ b/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{3973B07F-3667-4849-9D74-E6225E72C259}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{7BA4662B-3492-4B81-8081-ACA773397EA2}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{7C19E51C-41FA-46DE-9B69-35A11D9989A7}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{E81C4500-8F1F-45E5-92F1-B012E4D9FEB7}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="minispy.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="mspyLib.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RegistrationData.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="minispy.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/minispy/filter/mspyKern.h b/filesys/miniFilter/minispy/filter/mspyKern.h new file mode 100644 index 00000000..44bbc881 --- /dev/null +++ b/filesys/miniFilter/minispy/filter/mspyKern.h @@ -0,0 +1,412 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyKern.h + +Abstract: + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. + +Environment: + + Kernel mode + +--*/ +#ifndef __MSPYKERN_H__ +#define __MSPYKERN_H__ + +#include <fltKernel.h> +//#include <dontuse.h> +#include <suppress.h> +#include "minispy.h" + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +// +// Memory allocation tag +// + +#define SPY_TAG 'ypSM' + +// +// Win8 define for support of NPFS/MSFS +// Win7 define for support of new ECPs. +// Vista define for including transaction support, +// older ECPs +// + +#define MINISPY_WIN8 (NTDDI_VERSION >= NTDDI_WIN8) +#define MINISPY_WIN7 (NTDDI_VERSION >= NTDDI_WIN7) +#define MINISPY_VISTA (NTDDI_VERSION >= NTDDI_VISTA) +#define MINISPY_NOT_W2K (OSVER(NTDDI_VERSION) > NTDDI_WIN2K) + +// +// Define callback types for Vista +// + +#if MINISPY_VISTA + +// +// Dynamically imported Filter Mgr APIs +// + +typedef NTSTATUS +(*PFLT_SET_TRANSACTION_CONTEXT)( + _In_ PFLT_INSTANCE Instance, + _In_ PKTRANSACTION Transaction, + _In_ FLT_SET_CONTEXT_OPERATION Operation, + _In_ PFLT_CONTEXT NewContext, + _Outptr_opt_ PFLT_CONTEXT *OldContext + ); + +typedef NTSTATUS +(*PFLT_GET_TRANSACTION_CONTEXT)( + _In_ PFLT_INSTANCE Instance, + _In_ PKTRANSACTION Transaction, + _Outptr_ PFLT_CONTEXT *Context + ); + +typedef NTSTATUS +(*PFLT_ENLIST_IN_TRANSACTION)( + _In_ PFLT_INSTANCE Instance, + _In_ PKTRANSACTION Transaction, + _In_ PFLT_CONTEXT TransactionContext, + _In_ NOTIFICATION_MASK NotificationMask + ); + +// +// Flags for the known ECPs +// + +#define ECP_TYPE_FLAG_PREFETCH 0x00000001 + +#if MINISPY_WIN7 + +#define ECP_TYPE_FLAG_OPLOCK_KEY 0x00000002 +#define ECP_TYPE_FLAG_NFS 0x00000004 +#define ECP_TYPE_FLAG_SRV 0x00000008 + +#endif + +#define ADDRESS_STRING_BUFFER_SIZE 64 + +// +// Enumerate the ECPs MiniSpy supports +// + +typedef enum _ECP_TYPE { + + EcpPrefetchOpen, + EcpOplockKey, + EcpNfsOpen, + EcpSrvOpen, + + NumKnownEcps + +} ECP_TYPE; + +#endif + +//--------------------------------------------------------------------------- +// Global variables +//--------------------------------------------------------------------------- + +typedef struct _MINISPY_DATA { + + // + // The object that identifies this driver. + // + + PDRIVER_OBJECT DriverObject; + + // + // The filter that results from a call to + // FltRegisterFilter. + // + + PFLT_FILTER Filter; + + // + // Server port: user mode connects to this port + // + + PFLT_PORT ServerPort; + + // + // Client connection port: only one connection is allowed at a time., + // + + PFLT_PORT ClientPort; + + // + // List of buffers with data to send to user mode. + // + + KSPIN_LOCK OutputBufferLock; + LIST_ENTRY OutputBufferList; + + // + // Lookaside list used for allocating buffers. + // + + NPAGED_LOOKASIDE_LIST FreeBufferList; + + // + // Variables used to throttle how many records buffer we can use + // + + LONG MaxRecordsToAllocate; + __volatile LONG RecordsAllocated; + + // + // static buffer used for sending an "out-of-memory" message + // to user mode. + // + + __volatile LONG StaticBufferInUse; + + // + // We need to make sure this buffer aligns on a PVOID boundary because + // minispy casts this buffer to a RECORD_LIST structure. + // That can cause alignment faults unless the structure starts on the + // proper PVOID boundary + // + + PVOID OutOfMemoryBuffer[RECORD_SIZE/sizeof( PVOID )]; + + // + // Variable and lock for maintaining LogRecord sequence numbers. + // + + __volatile LONG LogSequenceNumber; + + // + // The name query method to use. By default, it is set to + // FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP, but it can be overridden + // by a setting in the registery. + // + + ULONG NameQueryMethod; + + // + // Global debug flags + // + + ULONG DebugFlags; + +#if MINISPY_VISTA + + // + // Dynamically imported Filter Mgr APIs + // + + PFLT_SET_TRANSACTION_CONTEXT PFltSetTransactionContext; + + PFLT_GET_TRANSACTION_CONTEXT PFltGetTransactionContext; + + PFLT_ENLIST_IN_TRANSACTION PFltEnlistInTransaction; + +#endif + +} MINISPY_DATA, *PMINISPY_DATA; + + +// +// Defines the minispy context structure +// + +typedef struct _MINISPY_TRANSACTION_CONTEXT { + ULONG Flags; + ULONG Count; + +}MINISPY_TRANSACTION_CONTEXT, *PMINISPY_TRANSACTION_CONTEXT; + +// +// This macro below is used to set the flags field in minispy's +// MINISPY_TRANSACTION_CONTEXT structure once it has been +// successfully enlisted in the transaction. +// + +#define MINISPY_ENLISTED_IN_TRANSACTION 0x01 + +// +// Minispy's global variables +// + +extern MINISPY_DATA MiniSpyData; + +#define DEFAULT_MAX_RECORDS_TO_ALLOCATE 500 +#define MAX_RECORDS_TO_ALLOCATE L"MaxRecords" + +#define DEFAULT_NAME_QUERY_METHOD FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP +#define NAME_QUERY_METHOD L"NameQueryMethod" + +// +// DebugFlag values +// + +#define SPY_DEBUG_PARSE_NAMES 0x00000001 + +//--------------------------------------------------------------------------- +// Registration structure +//--------------------------------------------------------------------------- + +extern const FLT_REGISTRATION FilterRegistration; + +//--------------------------------------------------------------------------- +// Function prototypes +//--------------------------------------------------------------------------- + +FLT_PREOP_CALLBACK_STATUS +SpyPreOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +SpyPostOperationCallback ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +NTSTATUS +SpyKtmNotificationCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_CONTEXT TransactionContext, + _In_ ULONG TransactionNotification + ); + +NTSTATUS +SpyFilterUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +SpyQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +VOID +SpyReadDriverParameters ( + _In_ PUNICODE_STRING RegistryPath + ); + +LONG +SpyExceptionFilter ( + _In_ PEXCEPTION_POINTERS ExceptionPointer, + _In_ BOOLEAN AccessingUserBuffer + ); + +//--------------------------------------------------------------------------- +// Memory allocation routines +//--------------------------------------------------------------------------- + +PRECORD_LIST +SpyAllocateBuffer ( + _Out_ PULONG RecordType + ); + +VOID +SpyFreeBuffer ( + _In_ PVOID Buffer + ); + +//--------------------------------------------------------------------------- +// Logging routines +//--------------------------------------------------------------------------- +PRECORD_LIST +SpyNewRecord ( + VOID + ); + +VOID +SpyFreeRecord ( + _In_ PRECORD_LIST Record + ); + +#if MINISPY_VISTA + +VOID +SpyParseEcps ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PRECORD_LIST RecordList, + _Inout_ PUNICODE_STRING EcpData + ); + +VOID +SpyBuildEcpDataString ( + _In_ PRECORD_LIST RecordList, + _Inout_ PUNICODE_STRING EcpData, + _In_reads_(NumKnownEcps) PVOID * ContextPointers + ); + +VOID +SpySetRecordNameAndEcpData ( + _Inout_ PLOG_RECORD LogRecord, + _In_ PUNICODE_STRING Name, + _In_opt_ PUNICODE_STRING EcpData + ); + +#else + +VOID +SpySetRecordName ( + _Inout_ PLOG_RECORD LogRecord, + _In_ PUNICODE_STRING Name + ); + +#endif + +VOID +SpyLogPreOperationData ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PRECORD_LIST RecordList + ); + +VOID +SpyLogPostOperationData ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PRECORD_LIST RecordList + ); + +VOID +SpyLogTransactionNotify ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PRECORD_LIST RecordList, + _In_ ULONG TransactionNotification + ); + +VOID +SpyLog ( + _In_ PRECORD_LIST RecordList + ); + +NTSTATUS +SpyGetLog ( + _Out_writes_bytes_to_(OutputBufferLength,*ReturnOutputBufferLength) PUCHAR OutputBuffer, + _In_ ULONG OutputBufferLength, + _Out_ PULONG ReturnOutputBufferLength + ); + +VOID +SpyEmptyOutputBufferList ( + VOID + ); + +VOID +SpyDeleteTxfContext ( + _Inout_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +#endif //__MSPYKERN_H__ + diff --git a/filesys/miniFilter/minispy/filter/mspyLib.c b/filesys/miniFilter/minispy/filter/mspyLib.c new file mode 100644 index 00000000..521e907b --- /dev/null +++ b/filesys/miniFilter/minispy/filter/mspyLib.c @@ -0,0 +1,1541 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyLib.c + +Abstract: + This contains library support routines for MiniSpy + +Environment: + + Kernel mode + +--*/ + +#include <initguid.h> +#include <stdio.h> + +#include "mspyKern.h" + +// +// Can't pull in wsk.h until after MINISPY_VISTA is defined +// + +#if MINISPY_VISTA +#include <ntifs.h> +#include <wsk.h> +#endif + +//--------------------------------------------------------------------------- +// Assign text sections for each routine. +//--------------------------------------------------------------------------- + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, SpyReadDriverParameters) +#if MINISPY_VISTA + #pragma alloc_text(PAGE, SpyBuildEcpDataString) + #pragma alloc_text(PAGE, SpyParseEcps) +#endif +#endif + +UCHAR TxNotificationToMinorCode ( + _In_ ULONG TxNotification + ) +/*++ + +Routine Description: + + This routine has been written to convert a transaction notification code + to an Irp minor code. This function is needed because RECORD_DATA has a + UCHAR field for the Irp minor code whereas TxNotification is ULONG. As + of now all this function does is compute log_base_2(TxNotification) + 1. + That fits our need for now but might have to be evolved later. This + function is intricately tied with the enumeration TRANSACTION_NOTIFICATION_CODES + in mspyLog.h and the case statements related to transactions in the function + PrintIrpCode (Minispy\User\mspyLog.c). + +Arguments: + + TxNotification - The transaction notification received. + +Return Value: + + 0 if TxNotification is 0; + log_base_2(TxNotification) + 1 otherwise. + +--*/ +{ + UCHAR count = 0; + + if (TxNotification == 0) + return 0; + + // + // This assert verifies if no more than one flag is set + // in the TxNotification variable. TxNotification flags are + // supposed to be mutually exclusive. The assert below verifies + // if the value of TxNotification is a power of 2. If it is not + // then we will break. + // + + FLT_ASSERT( !(( TxNotification ) & ( TxNotification - 1 )) ); + + while (TxNotification) { + + count++; + + TxNotification >>= 1; + + // + // If we hit this assert then we have more notification codes than + // can fit in a UCHAR. We need to revaluate our approach for + // storing minor codes now. + // + + FLT_ASSERT( count != 0 ); + } + + return ( count ); +} + + +//--------------------------------------------------------------------------- +// Log Record allocation routines +//--------------------------------------------------------------------------- + +PRECORD_LIST +SpyAllocateBuffer ( + _Out_ PULONG RecordType + ) +/*++ + +Routine Description: + + Allocates a new buffer from the MiniSpyData.FreeBufferList if there is + enough memory to do so and we have not exceed our maximum buffer + count. + + NOTE: Because there is no interlock between testing if we have exceeded + the record allocation limit and actually increment the in use + count it is possible to temporarily allocate one or two buffers + more then the limit. Because this is such a rare situation there + is not point to handling this. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + RecordType - Receives information on what type of record was allocated. + +Return Value: + + Pointer to the allocated buffer, or NULL if the allocation failed. + +--*/ +{ + PVOID newBuffer; + ULONG newRecordType = RECORD_TYPE_NORMAL; + + // + // See if we have room to allocate more buffers + // + + if (MiniSpyData.RecordsAllocated < MiniSpyData.MaxRecordsToAllocate) { + + InterlockedIncrement( &MiniSpyData.RecordsAllocated ); + + newBuffer = ExAllocateFromNPagedLookasideList( &MiniSpyData.FreeBufferList ); + + if (newBuffer == NULL) { + + // + // We failed to allocate the memory. Decrement our global count + // and return what type of memory we have. + // + + InterlockedDecrement( &MiniSpyData.RecordsAllocated ); + + newRecordType = RECORD_TYPE_FLAG_OUT_OF_MEMORY; + } + + } else { + + // + // No more room to allocate memory, return we didn't get a buffer + // and why. + // + + newRecordType = RECORD_TYPE_FLAG_EXCEED_MEMORY_ALLOWANCE; + newBuffer = NULL; + } + + *RecordType = newRecordType; + return newBuffer; +} + + +VOID +SpyFreeBuffer ( + _In_ PVOID Buffer + ) +/*++ + +Routine Description: + + Free an allocate buffer. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + Buffer - The buffer to free. + +Return Value: + + None. + +--*/ +{ + // + // Free the memory, update the counter + // + + InterlockedDecrement( &MiniSpyData.RecordsAllocated ); + ExFreeToNPagedLookasideList( &MiniSpyData.FreeBufferList, Buffer ); +} + + +//--------------------------------------------------------------------------- +// Logging routines +//--------------------------------------------------------------------------- + +PRECORD_LIST +SpyNewRecord ( + VOID + ) +/*++ + +Routine Description: + + Allocates a new RECORD_LIST structure if there is enough memory to do so. A + sequence number is updated for each request for a new record. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + None + +Return Value: + + Pointer to the RECORD_LIST allocated, or NULL if no memory is available. + +--*/ +{ + PRECORD_LIST newRecord; + ULONG initialRecordType; + + // + // Allocate the buffer + // + + newRecord = SpyAllocateBuffer( &initialRecordType ); + + if (newRecord == NULL) { + + // + // We could not allocate a record, see if the static buffer is + // in use. If not, we will use it + // + + if (!InterlockedExchange( &MiniSpyData.StaticBufferInUse, TRUE )) { + + newRecord = (PRECORD_LIST)MiniSpyData.OutOfMemoryBuffer; + initialRecordType |= RECORD_TYPE_FLAG_STATIC; + } + } + + // + // If we got a record (doesn't matter if it is static or not), init it + // + + if (newRecord != NULL) { + + // + // Init the new record + // + + newRecord->LogRecord.RecordType = initialRecordType; + newRecord->LogRecord.Length = sizeof(LOG_RECORD); + newRecord->LogRecord.SequenceNumber = InterlockedIncrement( &MiniSpyData.LogSequenceNumber ); + RtlZeroMemory( &newRecord->LogRecord.Data, sizeof( RECORD_DATA ) ); + } + + return( newRecord ); +} + + +VOID +SpyFreeRecord ( + _In_ PRECORD_LIST Record + ) +/*++ + +Routine Description: + + Free the given buffer + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + Record - the buffer to free + +Return Value: + + None. + +--*/ +{ + if (FlagOn(Record->LogRecord.RecordType,RECORD_TYPE_FLAG_STATIC)) { + + // + // This was our static buffer, mark it available. + // + + FLT_ASSERT(MiniSpyData.StaticBufferInUse); + MiniSpyData.StaticBufferInUse = FALSE; + + } else { + + SpyFreeBuffer( Record ); + } +} + +#if MINISPY_VISTA + +VOID +SpyBuildEcpDataString ( + _In_ PRECORD_LIST RecordList, + _Inout_ PUNICODE_STRING EcpData, + _In_reads_(NumKnownEcps) PVOID * ContextPointers + ) +/*++ + +Routine Description: + + Given the ECP presence data and context pointers located in SpyParseEcps, + uses _snwprintf to write a human-readable log output to a string provided. + +Arguments: + + RecordList - Pointer to the record, so we can see ECP count and masking + + EcpData - Pointer to string to receive formatted ECP log + + ContextPointers - Pointer to array of pointers, each of which is either NULL + or a context structure specific to a given type of ECP + +Return Value: + + None. + +--*/ +{ + ULONG knownCount = 0; + SHORT wcharsCopied = 0; + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + PWCHAR printPointer = EcpData->Buffer; + +#if MINISPY_WIN7 + TCHAR addressBuffer[ADDRESS_STRING_BUFFER_SIZE]; + ULONG addressBufferLen; + LONG addressConvStatus; +#endif + + PAGED_CODE(); + + FLT_ASSERT(NULL != ContextPointers); + + // + // Print initial ECP text + // + // NOTE: We don't check the return value of _snwprintf until the very end + // of this function. Because of this, if we run out of buffer space before we + // have printed all our information, we keep calling _snwprintf, although it + // does nothing. This is deliberate in the interest of keeping the code + // somewhat clean. + // + + #pragma prefast(push) + #pragma prefast(disable: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY __WARNING_BANNED_API_USAGE, "reviewed and safe usage") + // Prefast complains here because _snwprintf has some oddities. + // We've code reviewed to ensure safe usage. + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"[%d ECPs:", + recordData->EcpCount ); + + // + // Next, check all the known ECPs against the mask that was set in SpyParseEcps. + // If we recognize any of the ECPs, add their data to the log string. + // + +#if MINISPY_WIN7 + + // + // Oplock key ECP + // + + if (FlagOn( recordData->KnownEcpMask, ECP_TYPE_FLAG_OPLOCK_KEY )) { + + POPLOCK_KEY_ECP_CONTEXT oplockEcpContext = NULL; + LPGUID oplockKeyGuid; + UNICODE_STRING oplockKeyGuidString; + + knownCount++; + + // + // We now know this context pointer points to a + // OPLOCK_KEY_ECP_CONTEXT structure + // + + oplockEcpContext = (POPLOCK_KEY_ECP_CONTEXT) ContextPointers[EcpOplockKey]; + + FLT_ASSERT(NULL != oplockEcpContext); + + oplockKeyGuid = &oplockEcpContext->OplockKey; + + if (NT_SUCCESS(RtlStringFromGUID( oplockKeyGuid, + &oplockKeyGuidString ))) { + + // + // Format an output string to display the key in GUID form + // + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s OPLOCK KEY: %wZ;", + printPointer, + &oplockKeyGuidString ); + + RtlFreeUnicodeString( &oplockKeyGuidString ); + + } else { + + // + // Error processing the GUID + // + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s INVALID OPLOCK KEY;", + printPointer ); + } + } + + // + // NFS ECP + // + + if (FlagOn( recordData->KnownEcpMask, ECP_TYPE_FLAG_NFS )) { + + PNFS_OPEN_ECP_CONTEXT nfsEcpContext = NULL; + PUNICODE_STRING nfsShareNameString; + PSOCKADDR_STORAGE_NFS nfsClientSocketAddr; + + knownCount++; + + // + // We now know this context pointer points to a + // NFS_OPEN_ECP_CONTEXT structure + // + + nfsEcpContext= (PNFS_OPEN_ECP_CONTEXT) ContextPointers[EcpNfsOpen]; + + FLT_ASSERT(NULL != nfsEcpContext); + + nfsShareNameString = nfsEcpContext->ExportAlias; + nfsClientSocketAddr = nfsEcpContext->ClientSocketAddress; + + // + // Print the share name, if the string (optional) is present + // + + if (nfsShareNameString) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s NFS SHARE NAME: %wZ,", + printPointer, + nfsShareNameString ); + } + + FLT_ASSERT(nfsClientSocketAddr != NULL); + + addressConvStatus = STATUS_INVALID_PARAMETER; + addressBufferLen = ADDRESS_STRING_BUFFER_SIZE; + + if (nfsClientSocketAddr->ss_family == AF_INET) { + + PSOCKADDR_IN ipv4SocketAddr = (PSOCKADDR_IN) nfsClientSocketAddr; + + // + // Format IPv4 address and port + // + + addressConvStatus = RtlIpv4AddressToStringEx( + &ipv4SocketAddr->sin_addr, + ipv4SocketAddr->sin_port, + addressBuffer, + &addressBufferLen ); + + } else if (nfsClientSocketAddr->ss_family == AF_INET6) { + + PSOCKADDR_IN6 ipv6SocketAddr = (PSOCKADDR_IN6) nfsClientSocketAddr; + + // + // Format IPv6 address and port + // + + addressConvStatus = RtlIpv6AddressToStringEx( + &ipv6SocketAddr->sin6_addr, + 0, + ipv6SocketAddr->sin6_port, + addressBuffer, + &addressBufferLen ); + } + + // + // Print the address (and port) + // + + if ((STATUS_INVALID_PARAMETER != addressConvStatus) && + (0 < addressBufferLen)) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s NFS SOCKET ADDR: %S;", + printPointer, + addressBuffer ); + + } else { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s NFS INVALID SOCKET ADDR;", + printPointer ); + } + } + + // + // SRV ECP + // + + if (FlagOn( recordData->KnownEcpMask, ECP_TYPE_FLAG_SRV )) { + + PSRV_OPEN_ECP_CONTEXT srvEcpContext = NULL; + PUNICODE_STRING srvShareNameString; + PSOCKADDR_STORAGE_NFS srvClientSocketAddr; + + knownCount++; + + // + // We now know this context pointer points to a + // SRV_OPEN_ECP_CONTEXT structure + // + + srvEcpContext= (PSRV_OPEN_ECP_CONTEXT) ContextPointers[EcpSrvOpen]; + + FLT_ASSERT(NULL != srvEcpContext); + + srvShareNameString = srvEcpContext->ShareName; + srvClientSocketAddr = srvEcpContext->SocketAddress; + + // + // Print the share name, if the string is present + // + + if (srvShareNameString) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s SRV SHARE NAME: %wZ,", + printPointer, + srvShareNameString ); + } + + FLT_ASSERT(srvClientSocketAddr != NULL); + + addressConvStatus = STATUS_INVALID_PARAMETER; + addressBufferLen = ADDRESS_STRING_BUFFER_SIZE; + + // + // Print the address, whether it's IPv4 or IPv6 + // + + if (srvClientSocketAddr->ss_family == AF_INET) { + + PSOCKADDR_IN ipv4SocketAddr = (PSOCKADDR_IN) srvClientSocketAddr; + + // + // Format IPv4 address and port + // + + addressConvStatus = RtlIpv4AddressToStringEx( + &ipv4SocketAddr->sin_addr, + ipv4SocketAddr->sin_port, + addressBuffer, + &addressBufferLen ); + + } else if (srvClientSocketAddr->ss_family == AF_INET6) { + + PSOCKADDR_IN6 ipv6SocketAddr = (PSOCKADDR_IN6) srvClientSocketAddr; + + // + // Format IPv6 address and port + // + + addressConvStatus = RtlIpv6AddressToStringEx( + &ipv6SocketAddr->sin6_addr, + 0, + ipv6SocketAddr->sin6_port, + addressBuffer, + &addressBufferLen ); + } + + if ((STATUS_INVALID_PARAMETER != addressConvStatus) && + (0 < addressBufferLen)) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s SRV SOCKET ADDR: %S;", + printPointer, + addressBuffer ); + + } else { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s SRV INVALID SOCKET ADDR;", + printPointer ); + } + + // + // Print SRV flags + // + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s SRV FLAGS: %s%s%s;", + printPointer, + (srvEcpContext->OplockBlockState) ? L"B" : L"-", + (srvEcpContext->OplockAppState) ? L"A" : L"-", + (srvEcpContext->OplockFinalState) ? L"F" : L"-" ); + } + +#else + UNREFERENCED_PARAMETER( ContextPointers ); +#endif + + // + // Prefetch ECP + // + + if (FlagOn( recordData->KnownEcpMask, ECP_TYPE_FLAG_PREFETCH )) { + + knownCount++; + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s PREFETCH;", + printPointer ); + } + + // + // Print closing ECP text + // + + if (knownCount < recordData->EcpCount) { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s %d unknown ECPs]", + printPointer, + recordData->EcpCount - knownCount ); + + } else { + + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%s]", + printPointer ); + } + + // + // If wcharsCopied is negative, it means we maxed out our buffer + // and exited early. Otherwise, the length is the maximum space + // minus the leftover buffer space + // + + if (wcharsCopied >= 0) { + + EcpData->Length = wcharsCopied * sizeof(WCHAR); + + } else { + + // + // There wasn't enough buffer space, so manually truncate in a NULL + // + + EcpData->Length = MAX_NAME_SPACE_LESS_NULL; + EcpData->Buffer[MAX_NAME_WCHARS_LESS_NULL] = UNICODE_NULL; + } + + #pragma prefast(pop) +} + +VOID +SpyParseEcps ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PRECORD_LIST RecordList, + _Inout_ PUNICODE_STRING EcpData + ) + /*++ + +Routine Description: + + Extracts ECPs from the given callback data and logs them, + then calls SpyBuildEcpDataString to write a MiniSpy-specific + ECP log string. + +Arguments: + + Data - The Data structure that contains the information we want to record. + + RecordList - Pointer to the record, so we can set ECP count and masking + + EcpData - Pointer to string to receive formatted ECP log + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + PECP_LIST ecpList; + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + PVOID ecpContext = NULL; + GUID ecpGuid = {0}; + ULONG ecpContextSize = 0; + ULONG ecpFlag; + PVOID contextPointers[NumKnownEcps]; + UCHAR offset = 0; + + PAGED_CODE(); + + RtlZeroMemory( contextPointers, sizeof(PVOID) * NumKnownEcps ); + + // + // Try to get an ECP list pointer from filter manager + // + + status = FltGetEcpListFromCallbackData( MiniSpyData.Filter, + Data, + &ecpList ); + + // + // Even if the operation was successful, ecpList may be NULL + // if there are no ECPs attached to this operation, so we must + // make both checks + // + + if (NT_SUCCESS(status) && (NULL != ecpList)) { + + // + // Now ask filter manager for each ECP + // + + while (NT_SUCCESS( + FltGetNextExtraCreateParameter( MiniSpyData.Filter, + ecpList, + ecpContext, + (LPGUID) &ecpGuid, + &ecpContext, + &ecpContextSize ))) { + + // + // At this point, we have all the information we should need for a given + // ECP, but processing of ECPs is contingent on knowledge of their + // specific context structure. From here, ECP processing is driver-specific. + // + + // + // MiniSpy supports several system-defined ECPs. What follows is + // MiniSpy-specific code to log any known ECPs and produce some + // meaningful output for the user + // + + ecpFlag = 0; + + if (IsEqualGUID( &GUID_ECP_PREFETCH_OPEN, &ecpGuid )) { + + // + // Prefetch ECP + // + + ecpFlag = ECP_TYPE_FLAG_PREFETCH; + offset = EcpPrefetchOpen; + } + +#if MINISPY_WIN7 + + // + // There are three system-defined ECPs that are only available + // as of Windows 7 + // + else if (IsEqualGUID( &GUID_ECP_OPLOCK_KEY, &ecpGuid )) { + + // + // Oplock key ECP + // + + ecpFlag = ECP_TYPE_FLAG_OPLOCK_KEY; + offset = EcpOplockKey; + + } else if (IsEqualGUID( &GUID_ECP_NFS_OPEN, &ecpGuid )) { + + // + // NFS open ECP + // + + ecpFlag = ECP_TYPE_FLAG_NFS; + offset = EcpNfsOpen; + + } else if (IsEqualGUID( &GUID_ECP_SRV_OPEN, &ecpGuid )) { + + // + // SRV ECP + // + + ecpFlag = ECP_TYPE_FLAG_SRV; + offset = EcpSrvOpen; + } + +#endif + + // + // We don't accept user mode ECPs because of the potential + // for bad buffers + // + + if ((0 != ecpFlag) && + !FltIsEcpFromUserMode( MiniSpyData.Filter, ecpContext )) { + + // + // If ecpFlag was set, we found a MiniSpy-supported ECP. + // Make sure we have not already found an ECP of this type + // for this particular operation + // + + FLT_ASSERT(!FlagOn( recordData->KnownEcpMask, ecpFlag )); + + // + // Set the flag to indicate a given type of ECP was found + // + + recordData->KnownEcpMask |= ecpFlag; + + // + // Save the context pointer so we can get detailed data later + // + + contextPointers[offset] = ecpContext; + } + + // + // Increment the number of total ECPs (counting both known and unknown) + // + + recordData->EcpCount++; + } + + // + // Call the Minispy-specific function to format the ECP data string for + // output + // + + if (0 < recordData->EcpCount) { + + SpyBuildEcpDataString( RecordList, EcpData, contextPointers ); + } + } +} + +VOID +SpySetRecordNameAndEcpData( + _Inout_ PLOG_RECORD LogRecord, + _In_ PUNICODE_STRING Name, + _In_opt_ PUNICODE_STRING EcpData + ) +/*++ + +Routine Description: + + Sets the given file name in the LogRecord. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path. + +Arguments: + + LogRecord - The record in which to set the name. + + Name - The name to insert + + EcpData - A string of variable-length ECP data to insert + +Return Value: + + None. + +--*/ +{ + + PWCHAR printPointer = (PWCHAR)LogRecord->Name; + SHORT wcharsCopied; + USHORT stringLength; + + FLT_ASSERT(NULL != Name); + + // + // Put as much of the two strings as possible into the final buffer, + // name first, followed by ECP information (if any) + // + + if (NULL != EcpData) { + + #pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%wZ %wZ", + Name, + EcpData ); + + } else { + + #pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%wZ", + Name ); + } + + if (wcharsCopied >= 0) { + + stringLength = wcharsCopied * sizeof(WCHAR); + + } else { + + // + // There wasn't enough buffer space, so manually truncate in a NULL + // because we can't trust _snwprintf to do so in that case. + // + + stringLength = MAX_NAME_SPACE_LESS_NULL; + printPointer[MAX_NAME_WCHARS_LESS_NULL] = UNICODE_NULL; + } + + // + // We will always round up log-record length to sizeof(PVOID) so that + // the next log record starts on the right PVOID boundary to prevent + // IA64 alignment faults. The length of the record of course + // includes the additional NULL at the end. + // + + LogRecord->Length = ROUND_TO_SIZE( (LogRecord->Length + + stringLength + + sizeof( UNICODE_NULL )), + sizeof( PVOID ) ); + + FLT_ASSERT(LogRecord->Length <= MAX_LOG_RECORD_LENGTH); +} + +#else + +VOID +SpySetRecordName( + _Inout_ PLOG_RECORD LogRecord, + _In_ PUNICODE_STRING Name + ) +/*++ + +Routine Description: + + Sets the given file name in the LogRecord. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path. + +Arguments: + + LogRecord - The record in which to set the name. + + Name - The name to insert + +Return Value: + + None. + +--*/ +{ + + PWCHAR printPointer = (PWCHAR)LogRecord->Name; + SHORT wcharsCopied; + USHORT stringLength; + + FLT_ASSERT(NULL != Name); + + #pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage") + wcharsCopied = (SHORT) _snwprintf( printPointer, + MAX_NAME_WCHARS_LESS_NULL, + L"%wZ", + Name ); + + if (wcharsCopied >= 0) { + + stringLength = wcharsCopied * sizeof(WCHAR); + + } else { + + // + // There wasn't enough buffer space, so manually truncate in a NULL + // because we can't trust _snwprintf to do so in that case. + // + + stringLength = MAX_NAME_SPACE_LESS_NULL; + printPointer[MAX_NAME_WCHARS_LESS_NULL] = UNICODE_NULL; + } + + // + // We will always round up log-record length to sizeof(PVOID) so that + // the next log record starts on the right PVOID boundary to prevent + // IA64 alignment faults. The length of the record of course + // includes the additional NULL at the end. + // + + LogRecord->Length = ROUND_TO_SIZE( (LogRecord->Length + + stringLength + + sizeof( UNICODE_NULL )), + sizeof( PVOID ) ); + + FLT_ASSERT(LogRecord->Length <= MAX_LOG_RECORD_LENGTH); +} + +#endif + +VOID +SpyLogPreOperationData ( + _In_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PRECORD_LIST RecordList + ) +/*++ + +Routine Description: + + This is called from the pre-operation callback routine to copy the + necessary information into the log record. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path. + +Arguments: + + Data - The Data structure that contains the information we want to record. + + FltObjects - Pointer to the io objects involved in this operation. + + RecordList - Where we want to save the data + +Return Value: + + None. + +--*/ +{ + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + PDEVICE_OBJECT devObj; + NTSTATUS status; + + status = FltGetDeviceObject(FltObjects->Volume,&devObj); + if (NT_SUCCESS(status)) { + + ObDereferenceObject(devObj); + + } else { + + devObj = NULL; + } + + // + // Save the information we want + // + + recordData->CallbackMajorId = Data->Iopb->MajorFunction; + recordData->CallbackMinorId = Data->Iopb->MinorFunction; + recordData->IrpFlags = Data->Iopb->IrpFlags; + recordData->Flags = Data->Flags; + + recordData->DeviceObject = (FILE_ID)devObj; + recordData->FileObject = (FILE_ID)FltObjects->FileObject; + recordData->Transaction = (FILE_ID)FltObjects->Transaction; + recordData->ProcessId = (FILE_ID)PsGetCurrentProcessId(); + recordData->ThreadId = (FILE_ID)PsGetCurrentThreadId(); + + recordData->Arg1 = Data->Iopb->Parameters.Others.Argument1; + recordData->Arg2 = Data->Iopb->Parameters.Others.Argument2; + recordData->Arg3 = Data->Iopb->Parameters.Others.Argument3; + recordData->Arg4 = Data->Iopb->Parameters.Others.Argument4; + recordData->Arg5 = Data->Iopb->Parameters.Others.Argument5; + recordData->Arg6.QuadPart = Data->Iopb->Parameters.Others.Argument6.QuadPart; + + KeQuerySystemTime( &recordData->OriginatingTime ); +} + + +VOID +SpyLogPostOperationData ( + _In_ PFLT_CALLBACK_DATA Data, + _Inout_ PRECORD_LIST RecordList + ) +/*++ + +Routine Description: + + This is called from the post-operation callback routine to copy the + necessary information into the log record. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level. + +Arguments: + + Data - The Data structure that contains the information we want to record. + + RecordList - Where we want to save the data + +Return Value: + + None. + +--*/ +{ + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + + recordData->Status = Data->IoStatus.Status; + recordData->Information = Data->IoStatus.Information; + KeQuerySystemTime( &recordData->CompletionTime ); +} + + +VOID +SpyLogTransactionNotify ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Inout_ PRECORD_LIST RecordList, + _In_ ULONG TransactionNotification + ) +/*++ + +Routine Description: + + This routine logs the transaction notification. + +Arguments: + + FltObjects - Pointer to the io objects involved in this operation. + + RecordList - Where we want to save the data + + TransactionNotification - Notification for this transaction. + +Return Value: + + None. + +--*/ +{ + PRECORD_DATA recordData = &RecordList->LogRecord.Data; + PDEVICE_OBJECT devObj; + NTSTATUS status; + + status = FltGetDeviceObject(FltObjects->Volume,&devObj); + if (NT_SUCCESS(status)) { + + ObDereferenceObject(devObj); + + } else { + + devObj = NULL; + } + + + recordData->CallbackMajorId = IRP_MJ_TRANSACTION_NOTIFY; + recordData->CallbackMinorId = TxNotificationToMinorCode(TransactionNotification); + + recordData->DeviceObject = (FILE_ID)devObj; + recordData->FileObject = (FILE_ID)FltObjects->FileObject; + recordData->Transaction = (FILE_ID)FltObjects->Transaction; + recordData->ProcessId = (FILE_ID)PsGetCurrentProcessId(); + recordData->ThreadId = (FILE_ID)PsGetCurrentThreadId(); + + KeQuerySystemTime( &recordData->OriginatingTime ); +} + + +VOID +SpyLog ( + _In_ PRECORD_LIST RecordList + ) +/*++ + +Routine Description: + + This routine inserts the given log record into the list to be sent + to the user mode application. + + NOTE: This code must be NON-PAGED because it can be called on the + paging path or at DPC level and uses a spin-lock + +Arguments: + + RecordList - The record to append to the MiniSpyData.OutputBufferList + +Return Value: + + The function returns STATUS_SUCCESS. + + + +--*/ +{ + KIRQL oldIrql; + + KeAcquireSpinLock(&MiniSpyData.OutputBufferLock, &oldIrql); + InsertTailList(&MiniSpyData.OutputBufferList, &RecordList->List); + KeReleaseSpinLock(&MiniSpyData.OutputBufferLock, oldIrql); +} + + +NTSTATUS +SpyGetLog ( + _Out_writes_bytes_to_(OutputBufferLength,*ReturnOutputBufferLength) PUCHAR OutputBuffer, + _In_ ULONG OutputBufferLength, + _Out_ PULONG ReturnOutputBufferLength + ) +/*++ + +Routine Description: + This function fills OutputBuffer with as many LOG_RECORDs as possible. + The LOG_RECORDs are variable sizes and are tightly packed in the + OutputBuffer. + + NOTE: This code must be NON-PAGED because it uses a spin-lock. + +Arguments: + OutputBuffer - The user's buffer to fill with the log data we have + collected + + OutputBufferLength - The size in bytes of OutputBuffer + + ReturnOutputBufferLength - The amount of data actually written into the + OutputBuffer. + +Return Value: + STATUS_SUCCESS if some records were able to be written to the OutputBuffer. + + STATUS_NO_MORE_ENTRIES if we have no data to return. + + STATUS_BUFFER_TOO_SMALL if the OutputBuffer is too small to + hold even one record and we have data to return. + +--*/ +{ + PLIST_ENTRY pList; + ULONG bytesWritten = 0; + PLOG_RECORD pLogRecord; + NTSTATUS status = STATUS_NO_MORE_ENTRIES; + PRECORD_LIST pRecordList; + KIRQL oldIrql; + BOOLEAN recordsAvailable = FALSE; + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + + while (!IsListEmpty( &MiniSpyData.OutputBufferList ) && (OutputBufferLength > 0)) { + + // + // Mark we have records + // + + recordsAvailable = TRUE; + + // + // Get the next available record + // + + pList = RemoveHeadList( &MiniSpyData.OutputBufferList ); + + pRecordList = CONTAINING_RECORD( pList, RECORD_LIST, List ); + + pLogRecord = &pRecordList->LogRecord; + + // + // If no filename was set then make it into a NULL file name. + // + + if (REMAINING_NAME_SPACE( pLogRecord ) == MAX_NAME_SPACE) { + + // + // We don't have a name, so return an empty string. + // We have to always start a new log record on a PVOID aligned boundary. + // + + pLogRecord->Length += ROUND_TO_SIZE( sizeof( UNICODE_NULL ), sizeof( PVOID ) ); + pLogRecord->Name[0] = UNICODE_NULL; + } + + // + // Put it back if we've run out of room. + // + + if (OutputBufferLength < pLogRecord->Length) { + + InsertHeadList( &MiniSpyData.OutputBufferList, pList ); + break; + } + + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); + + // + // The lock is released, return the data, adjust pointers. + // Protect access to raw user-mode OutputBuffer with an exception handler + // + + try { + RtlCopyMemory( OutputBuffer, pLogRecord, pLogRecord->Length ); + } except (SpyExceptionFilter( GetExceptionInformation(), TRUE )) { + + // + // Put the record back in + // + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + InsertHeadList( &MiniSpyData.OutputBufferList, pList ); + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); + + return GetExceptionCode(); + + } + + bytesWritten += pLogRecord->Length; + + OutputBufferLength -= pLogRecord->Length; + + OutputBuffer += pLogRecord->Length; + + SpyFreeRecord( pRecordList ); + + // + // Relock the list + // + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + } + + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); + + // + // Set proper status + // + + if ((bytesWritten == 0) && recordsAvailable) { + + // + // There were records to be sent up but + // there was not enough room in the buffer. + // + + status = STATUS_BUFFER_TOO_SMALL; + + } else if (bytesWritten > 0) { + + // + // We were able to write some data to the output buffer, + // so this was a success. + // + + status = STATUS_SUCCESS; + } + + *ReturnOutputBufferLength = bytesWritten; + + return status; +} + + +VOID +SpyEmptyOutputBufferList ( + VOID + ) +/*++ + +Routine Description: + + This routine frees all the remaining log records in the OutputBufferList + that are not going to get sent up to the user mode application since + MiniSpy is shutting down. + + NOTE: This code must be NON-PAGED because it uses a spin-lock + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY pList; + PRECORD_LIST pRecordList; + KIRQL oldIrql; + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + + while (!IsListEmpty( &MiniSpyData.OutputBufferList )) { + + pList = RemoveHeadList( &MiniSpyData.OutputBufferList ); + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); + + pRecordList = CONTAINING_RECORD( pList, RECORD_LIST, List ); + + SpyFreeRecord( pRecordList ); + + KeAcquireSpinLock( &MiniSpyData.OutputBufferLock, &oldIrql ); + } + + KeReleaseSpinLock( &MiniSpyData.OutputBufferLock, oldIrql ); +} + +//--------------------------------------------------------------------------- +// Logging routines +//--------------------------------------------------------------------------- + +VOID +SpyReadDriverParameters ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the MiniSpy-specific parameters from + the registry. These values will be found in the registry location + indicated by the RegistryPath passed in. + + This processes the following registry keys: + hklm\system\CurrentControlSet\Services\Minispy\MaxRecords + hklm\system\CurrentControlSet\Services\Minispy\NameQueryMethod + + +Arguments: + + RegistryPath - the path key which contains the values that are + the MiniSpy parameters + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + PKEY_VALUE_PARTIAL_INFORMATION pValuePartialInfo; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + // + // Open the registry + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + return; + } + + // + // Read the MaxRecordsToAllocate entry from the registry + // + + RtlInitUnicodeString( &valueName, MAX_RECORDS_TO_ALLOCATE ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + pValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; + FLT_ASSERT( pValuePartialInfo->Type == REG_DWORD ); + MiniSpyData.MaxRecordsToAllocate = *((PLONG)&(pValuePartialInfo->Data)); + } + + // + // Read the NameQueryMethod entry from the registry + // + + RtlInitUnicodeString( &valueName, NAME_QUERY_METHOD ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + pValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; + FLT_ASSERT( pValuePartialInfo->Type == REG_DWORD ); + MiniSpyData.NameQueryMethod = *((PLONG)&(pValuePartialInfo->Data)); + } + + ZwClose(driverRegKey); +} + diff --git a/filesys/miniFilter/minispy/inc/minispy.h b/filesys/miniFilter/minispy/inc/minispy.h new file mode 100644 index 00000000..a6043e62 --- /dev/null +++ b/filesys/miniFilter/minispy/inc/minispy.h @@ -0,0 +1,251 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + minispy.h + +Abstract: + + Header file which contains the structures, type definitions, + and constants that are shared between the kernel mode driver, + minispy.sys, and the user mode executable, minispy.exe. + +Environment: + + Kernel and user mode + +--*/ +#ifndef __MINISPY_H__ +#define __MINISPY_H__ + + +// +// FltMgr's IRP major codes +// + +#define IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION ((UCHAR)-1) +#define IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION ((UCHAR)-2) +#define IRP_MJ_ACQUIRE_FOR_MOD_WRITE ((UCHAR)-3) +#define IRP_MJ_RELEASE_FOR_MOD_WRITE ((UCHAR)-4) +#define IRP_MJ_ACQUIRE_FOR_CC_FLUSH ((UCHAR)-5) +#define IRP_MJ_RELEASE_FOR_CC_FLUSH ((UCHAR)-6) +#define IRP_MJ_NOTIFY_STREAM_FO_CREATION ((UCHAR)-7) + +#define IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE ((UCHAR)-13) +#define IRP_MJ_NETWORK_QUERY_OPEN ((UCHAR)-14) +#define IRP_MJ_MDL_READ ((UCHAR)-15) +#define IRP_MJ_MDL_READ_COMPLETE ((UCHAR)-16) +#define IRP_MJ_PREPARE_MDL_WRITE ((UCHAR)-17) +#define IRP_MJ_MDL_WRITE_COMPLETE ((UCHAR)-18) +#define IRP_MJ_VOLUME_MOUNT ((UCHAR)-19) +#define IRP_MJ_VOLUME_DISMOUNT ((UCHAR)-20) + +// +// My own definition for transaction notify command +// + +#define IRP_MJ_TRANSACTION_NOTIFY ((UCHAR)-40) + + +// +// Version definition +// + +#define MINISPY_MAJ_VERSION 2 +#define MINISPY_MIN_VERSION 0 + +typedef struct _MINISPYVER { + + USHORT Major; + USHORT Minor; + +} MINISPYVER, *PMINISPYVER; + +// +// Name of minispy's communication server port +// + +#define MINISPY_PORT_NAME L"\\MiniSpyPort" + +// +// Local definitions for passing parameters between the filter and user mode +// + +typedef ULONG_PTR FILE_ID; +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; + +// +// The maximum size of a record that can be passed from the filter +// + +#define RECORD_SIZE 1024 + +// +// This defines the type of record buffer this is along with certain flags. +// + +#define RECORD_TYPE_NORMAL 0x00000000 +#define RECORD_TYPE_FILETAG 0x00000004 + +#define RECORD_TYPE_FLAG_STATIC 0x80000000 +#define RECORD_TYPE_FLAG_EXCEED_MEMORY_ALLOWANCE 0x20000000 +#define RECORD_TYPE_FLAG_OUT_OF_MEMORY 0x10000000 +#define RECORD_TYPE_FLAG_MASK 0xffff0000 + +// +// The fixed data received for RECORD_TYPE_NORMAL +// + +typedef struct _RECORD_DATA { + + LARGE_INTEGER OriginatingTime; + LARGE_INTEGER CompletionTime; + + FILE_ID DeviceObject; + FILE_ID FileObject; + FILE_ID Transaction; + + FILE_ID ProcessId; + FILE_ID ThreadId; + + ULONG_PTR Information; + + NTSTATUS Status; + + ULONG IrpFlags; + ULONG Flags; + + UCHAR CallbackMajorId; + UCHAR CallbackMinorId; + UCHAR Reserved[2]; // Alignment on IA64 + + PVOID Arg1; + PVOID Arg2; + PVOID Arg3; + PVOID Arg4; + PVOID Arg5; + LARGE_INTEGER Arg6; + + ULONG EcpCount; + ULONG KnownEcpMask; + +} RECORD_DATA, *PRECORD_DATA; + +// +// What information we actually log. +// + +#pragma warning(push) +#pragma warning(disable:4200) // disable warnings for structures with zero length arrays. + +typedef struct _LOG_RECORD { + + + ULONG Length; // Length of log record. This Does not include + ULONG SequenceNumber; // space used by other members of RECORD_LIST + + ULONG RecordType; // The type of log record this is. + ULONG Reserved; // For alignment on IA64 + + RECORD_DATA Data; + WCHAR Name[]; // This is a null terminated string + +} LOG_RECORD, *PLOG_RECORD; + +#pragma warning(pop) + +// +// How the mini-filter manages the log records. +// + +typedef struct _RECORD_LIST { + + LIST_ENTRY List; + + // + // Must always be last item. See MAX_LOG_RECORD_LENGTH macro below. + // Must be aligned on PVOID boundary in this structure. This is because the + // log records are going to be packed one after another & accessed directly + // Size of log record must also be multiple of PVOID size to avoid alignment + // faults while accessing the log records on IA64 + // + + LOG_RECORD LogRecord; + +} RECORD_LIST, *PRECORD_LIST; + +// +// Defines the commands between the utility and the filter +// + +typedef enum _MINISPY_COMMAND { + + GetMiniSpyLog, + GetMiniSpyVersion + +} MINISPY_COMMAND; + +// +// Defines the command structure between the utility and the filter. +// + +#pragma warning(push) +#pragma warning(disable:4200) // disable warnings for structures with zero length arrays. + +typedef struct _COMMAND_MESSAGE { + MINISPY_COMMAND Command; + ULONG Reserved; // Alignment on IA64 + UCHAR Data[]; +} COMMAND_MESSAGE, *PCOMMAND_MESSAGE; + +#pragma warning(pop) + +// +// The maximum number of BYTES that can be used to store the file name in the +// RECORD_LIST structure +// + +#define MAX_NAME_SPACE ROUND_TO_SIZE( (RECORD_SIZE - sizeof(RECORD_LIST)), sizeof( PVOID )) + +// +// The maximum space, in bytes and WCHARs, available for the name (and ECP +// if present) string, not including the space that must be reserved for a NULL +// + +#define MAX_NAME_SPACE_LESS_NULL (MAX_NAME_SPACE - sizeof(UNICODE_NULL)) +#define MAX_NAME_WCHARS_LESS_NULL MAX_NAME_SPACE_LESS_NULL / sizeof(WCHAR) + +// +// Returns the number of BYTES unused in the RECORD_LIST structure. Note that +// LogRecord->Length already contains the size of LOG_RECORD which is why we +// have to remove it. +// + +#define REMAINING_NAME_SPACE(LogRecord) \ + (FLT_ASSERT((LogRecord)->Length >= sizeof(LOG_RECORD)), \ + (USHORT)(MAX_NAME_SPACE - ((LogRecord)->Length - sizeof(LOG_RECORD)))) + +#define MAX_LOG_RECORD_LENGTH (RECORD_SIZE - FIELD_OFFSET( RECORD_LIST, LogRecord )) + + +// +// Macros available in kernel mode which are not available in user mode +// + +#ifndef Add2Ptr +#define Add2Ptr(P,I) ((PVOID)((PUCHAR)(P) + (I))) +#endif + +#ifndef ROUND_TO_SIZE +#define ROUND_TO_SIZE(_length, _alignment) \ + (((_length) + ((_alignment)-1)) & ~((_alignment) - 1)) +#endif + +#ifndef FlagOn +#define FlagOn(_F,_SF) ((_F) & (_SF)) +#endif + +#endif /* __MINISPY_H__ */ + diff --git a/filesys/miniFilter/minispy/minispy.inf b/filesys/miniFilter/minispy/minispy.inf new file mode 100644 index 00000000..88cdcd40 --- /dev/null +++ b/filesys/miniFilter/minispy/minispy.inf @@ -0,0 +1,111 @@ +;;; +;;; Minispy +;;; +;;; +;;; Copyright (c) 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.0 +CatalogFile = minispy.cat + + +[DestinationDirs] +DefaultDestDir = 12 +Minispy.DriverFiles = 12 ;%windir%\system32\drivers +Minispy.UserFiles = 10,FltMgr ;%windir%\FltMgr + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = Minispy.DriverFiles, Minispy.UserFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,Minispy.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = Minispy.DriverFiles, Minispy.UserFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[Minispy.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = FltMgr +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = Minispy.AddRegistry + +; +; Registry Modifications +; + +[Minispy.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% +HKR,"Instances\"%Instance2.Name%,"Altitude",0x00000000,%Instance2.Altitude% +HKR,"Instances\"%Instance2.Name%,"Flags",0x00010001,%Instance2.Flags% +HKR,"Instances\"%Instance3.Name%,"Altitude",0x00000000,%Instance3.Altitude% +HKR,"Instances\"%Instance3.Name%,"Flags",0x00010001,%Instance3.Flags% + +; +; Copy Files +; + +[Minispy.DriverFiles] +%DriverName%.sys + +[Minispy.UserFiles] +%UserAppName%.exe + +[SourceDisksFiles] +minispy.sys = 1,, +minispy.exe = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Minispy mini-filter driver" +ServiceName = "Minispy" +DriverName = "minispy" +UserAppName = "minispy" +DiskId1 = "Minispy Device Installation Disk" + +;Instances specific information. +DefaultInstance = "Minispy - Top Instance" +Instance1.Name = "Minispy - Middle Instance" +Instance1.Altitude = "370000" +Instance1.Flags = 0x1 ; Suppress automatic attachments +Instance2.Name = "Minispy - Bottom Instance" +Instance2.Altitude = "361000" +Instance2.Flags = 0x1 ; Suppress automatic attachments +Instance3.Name = "Minispy - Top Instance" +Instance3.Altitude = "385100" +Instance3.Flags = 0x1 ; Suppress automatic attachments diff --git a/filesys/miniFilter/minispy/minispy.sln b/filesys/miniFilter/minispy/minispy.sln new file mode 100644 index 00000000..f6bc3c1c --- /dev/null +++ b/filesys/miniFilter/minispy/minispy.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{3B396921-0231-4B6C-B5E5-C7695F992BA7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{32B4D5F7-B865-467E-9344-627DDBB25267}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "filter\minispy.vcxproj", "{99B46F3E-1CC2-4689-8D3E-80CCBD448E39}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "user\minispy.vcxproj", "{3EFB308B-ED6C-42FE-9140-9883674EA0A2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|Win32.ActiveCfg = Debug|Win32 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|Win32.Build.0 = Debug|Win32 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|Win32.ActiveCfg = Release|Win32 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|Win32.Build.0 = Release|Win32 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|x64.ActiveCfg = Debug|x64 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Debug|x64.Build.0 = Debug|x64 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|x64.ActiveCfg = Release|x64 + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39}.Release|x64.Build.0 = Release|x64 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|Win32.ActiveCfg = Debug|Win32 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|Win32.Build.0 = Debug|Win32 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|Win32.ActiveCfg = Release|Win32 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|Win32.Build.0 = Release|Win32 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|x64.ActiveCfg = Debug|x64 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Debug|x64.Build.0 = Debug|x64 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|x64.ActiveCfg = Release|x64 + {3EFB308B-ED6C-42FE-9140-9883674EA0A2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {99B46F3E-1CC2-4689-8D3E-80CCBD448E39} = {3B396921-0231-4B6C-B5E5-C7695F992BA7} + {3EFB308B-ED6C-42FE-9140-9883674EA0A2} = {32B4D5F7-B865-467E-9344-627DDBB25267} + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/minispy/user/minispy.vcxproj b/filesys/miniFilter/minispy/user/minispy.vcxproj new file mode 100644 index 00000000..7903c881 --- /dev/null +++ b/filesys/miniFilter/minispy/user/minispy.vcxproj @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{3EFB308B-ED6C-42FE-9140-9883674EA0A2}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{66B7506B-24F4-4F68-9941-68114AD91305}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>minispy</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>minispy</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>minispy</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>minispy</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="mspyLog.c" /> + <ClCompile Include="mspyUser.c" /> + <ResourceCompile Include="mspyUser.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters b/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters new file mode 100644 index 00000000..dff75ebd --- /dev/null +++ b/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{FE182DA6-B396-42B5-BA36-09DA649AD386}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{72009667-0B4E-4D0D-9548-CB2182BD6769}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{9B01DDB6-745D-4785-BD95-F02013721E53}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="mspyLog.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="mspyUser.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="mspyUser.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/minispy/user/mspyLog.c b/filesys/miniFilter/minispy/user/mspyLog.c new file mode 100644 index 00000000..758efe2d --- /dev/null +++ b/filesys/miniFilter/minispy/user/mspyLog.c @@ -0,0 +1,1193 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyLog.c + +Abstract: + + This module contains functions used to retrieve and see the log records + recorded by MiniSpy.sys. + +Environment: + + User mode + +--*/ + +#include <DriverSpecs.h> +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include <stdio.h> +#include <windows.h> +#include <stdlib.h> +#include <winioctl.h> +#include "mspyLog.h" + +#define TIME_BUFFER_LENGTH 20 +#define TIME_ERROR "time error" + +#define POLL_INTERVAL 200 // 200 milliseconds + +BOOLEAN +TranslateFileTag( + _In_ PLOG_RECORD logRecord + ) +/*++ + +Routine Description: + + If this is a mount point reparse point, move the given name string to the + correct position in the log record structure so it will be displayed + by the common routines. + +Arguments: + + logRecord - The log record to update + +Return Value: + + TRUE - if this is a mount point reparse point + FALSE - otherwise + +--*/ +{ + PFLT_TAG_DATA_BUFFER TagData; + ULONG Length; + + // + // The reparse data structure starts in the NAME field, point to it. + // + + TagData = (PFLT_TAG_DATA_BUFFER) &logRecord->Name[0]; + + // + // See if MOUNT POINT tag + // + + if (TagData->FileTag == IO_REPARSE_TAG_MOUNT_POINT) { + + // + // calculate how much to copy + // + + Length = min( MAX_NAME_SPACE - sizeof(UNICODE_NULL), TagData->MountPointReparseBuffer.SubstituteNameLength ); + + // + // Position the reparse name at the proper position in the buffer. + // Note that we are doing an overlapped copy + // + + MoveMemory( &logRecord->Name[0], + TagData->MountPointReparseBuffer.PathBuffer, + Length ); + + logRecord->Name[Length/sizeof(WCHAR)] = UNICODE_NULL; + return TRUE; + } + + return FALSE; +} + + +DWORD +WINAPI +RetrieveLogRecords( + _In_ LPVOID lpParameter + ) +/*++ + +Routine Description: + + This runs as a separate thread. Its job is to retrieve log records + from the filter and then output them + +Arguments: + + lpParameter - Contains context structure for synchronizing with the + main program thread. + +Return Value: + + The thread successfully terminated + +--*/ +{ + PLOG_CONTEXT context = (PLOG_CONTEXT)lpParameter; + DWORD bytesReturned = 0; + DWORD used; + PVOID alignedBuffer[BUFFER_SIZE/sizeof( PVOID )]; + PCHAR buffer = (PCHAR) alignedBuffer; + HRESULT hResult; + PLOG_RECORD pLogRecord; + PRECORD_DATA pRecordData; + COMMAND_MESSAGE commandMessage; + + //printf("Log: Starting up\n"); + +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant + + while (TRUE) { + +#pragma warning(pop) + + // + // Check to see if we should shut down. + // + + if (context->CleaningUp) { + + break; + } + + // + // Request log data from MiniSpy. + // + + commandMessage.Command = GetMiniSpyLog; + + hResult = FilterSendMessage( context->Port, + &commandMessage, + sizeof( COMMAND_MESSAGE ), + buffer, + sizeof(alignedBuffer), + &bytesReturned ); + + if (IS_ERROR( hResult )) { + + if (HRESULT_FROM_WIN32( ERROR_INVALID_HANDLE ) == hResult) { + + printf( "The kernel component of minispy has unloaded. Exiting\n" ); + ExitProcess( 0 ); + } else { + + if (hResult != HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS )) { + + printf( "UNEXPECTED ERROR received: %x\n", hResult ); + } + + Sleep( POLL_INTERVAL ); + } + + continue; + } + + // + // Buffer is filled with a series of LOG_RECORD structures, one + // right after another. Each LOG_RECORD says how long it is, so + // we know where the next LOG_RECORD begins. + // + + pLogRecord = (PLOG_RECORD) buffer; + used = 0; + + // + // Logic to write record to screen and/or file + // + + for (;;) { + + if (used+FIELD_OFFSET(LOG_RECORD,Name) > bytesReturned) { + + break; + } + + if (pLogRecord->Length < (sizeof(LOG_RECORD)+sizeof(WCHAR))) { + + printf( "UNEXPECTED LOG_RECORD->Length: length=%d expected>=%d\n", + pLogRecord->Length, + (sizeof(LOG_RECORD)+sizeof(WCHAR))); + + break; + } + + used += pLogRecord->Length; + + if (used > bytesReturned) { + + printf( "UNEXPECTED LOG_RECORD size: used=%d bytesReturned=%d\n", + used, + bytesReturned); + + break; + } + + pRecordData = &pLogRecord->Data; + + // + // See if a reparse point entry + // + + if (FlagOn(pLogRecord->RecordType,RECORD_TYPE_FILETAG)) { + + if (!TranslateFileTag( pLogRecord )){ + + // + // If this is a reparse point that can't be interpreted, move on. + // + + pLogRecord = (PLOG_RECORD)Add2Ptr(pLogRecord,pLogRecord->Length); + continue; + } + } + + if (context->LogToScreen) { + + ScreenDump( pLogRecord->SequenceNumber, + pLogRecord->Name, + pRecordData ); + } + + if (context->LogToFile) { + + FileDump( pLogRecord->SequenceNumber, + pLogRecord->Name, + pRecordData, + context->OutputFile ); + } + + // + // The RecordType could also designate that we are out of memory + // or hit our program defined memory limit, so check for these + // cases. + // + + if (FlagOn(pLogRecord->RecordType,RECORD_TYPE_FLAG_OUT_OF_MEMORY)) { + + if (context->LogToScreen) { + + printf( "M: %08X System Out of Memory\n", + pLogRecord->SequenceNumber ); + } + + if (context->LogToFile) { + + fprintf( context->OutputFile, + "M:\t0x%08X\tSystem Out of Memory\n", + pLogRecord->SequenceNumber ); + } + + } else if (FlagOn(pLogRecord->RecordType,RECORD_TYPE_FLAG_EXCEED_MEMORY_ALLOWANCE)) { + + if (context->LogToScreen) { + + printf( "M: %08X Exceeded Mamimum Allowed Memory Buffers\n", + pLogRecord->SequenceNumber ); + } + + if (context->LogToFile) { + + fprintf( context->OutputFile, + "M:\t0x%08X\tExceeded Mamimum Allowed Memory Buffers\n", + pLogRecord->SequenceNumber ); + } + } + + // + // Move to next LOG_RECORD + // + + pLogRecord = (PLOG_RECORD)Add2Ptr(pLogRecord,pLogRecord->Length); + } + + // + // If we didn't get any data, pause for 1/2 second + // + + if (bytesReturned == 0) { + + Sleep( POLL_INTERVAL ); + } + } + + printf( "Log: Shutting down\n" ); + ReleaseSemaphore( context->ShutDown, 1, NULL ); + printf( "Log: All done\n" ); + return 0; +} + + +VOID +PrintIrpCode( + _In_ UCHAR MajorCode, + _In_ UCHAR MinorCode, + _In_opt_ FILE *OutputFile, + _In_ BOOLEAN PrintMajorCode +) +/*++ + +Routine Description: + + Display the operation code + +Arguments: + + MajorCode - Major function code of operation + + MinorCode - Minor function code of operation + + OutputFile - If writing to a file (not the screen) the handle for that file + + PrintMajorCode - Only used when printing to the display: + TRUE - if we want to display the MAJOR CODE + FALSE - if we want to display the MINOR code + +Return Value: + + None + +--*/ +{ + CHAR *irpMajorString, *irpMinorString = NULL; + CHAR errorBuf[128]; + + switch (MajorCode) { + case IRP_MJ_CREATE: + irpMajorString = IRP_MJ_CREATE_STRING; + break; + case IRP_MJ_CREATE_NAMED_PIPE: + irpMajorString = IRP_MJ_CREATE_NAMED_PIPE_STRING; + break; + case IRP_MJ_CLOSE: + irpMajorString = IRP_MJ_CLOSE_STRING; + break; + case IRP_MJ_READ: + irpMajorString = IRP_MJ_READ_STRING; + switch (MinorCode) { + case IRP_MN_NORMAL: + irpMinorString = IRP_MN_NORMAL_STRING; + break; + case IRP_MN_DPC: + irpMinorString = IRP_MN_DPC_STRING; + break; + case IRP_MN_MDL: + irpMinorString = IRP_MN_MDL_STRING; + break; + case IRP_MN_COMPLETE: + irpMinorString = IRP_MN_COMPLETE_STRING; + break; + case IRP_MN_COMPRESSED: + irpMinorString = IRP_MN_COMPRESSED_STRING; + break; + case IRP_MN_MDL_DPC: + irpMinorString = IRP_MN_MDL_DPC_STRING; + break; + case IRP_MN_COMPLETE_MDL: + irpMinorString = IRP_MN_COMPLETE_MDL_STRING; + break; + case IRP_MN_COMPLETE_MDL_DPC: + irpMinorString = IRP_MN_COMPLETE_MDL_DPC_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_WRITE: + irpMajorString = IRP_MJ_WRITE_STRING; + switch (MinorCode) { + case IRP_MN_NORMAL: + irpMinorString = IRP_MN_NORMAL_STRING; + break; + case IRP_MN_DPC: + irpMinorString = IRP_MN_DPC_STRING; + break; + case IRP_MN_MDL: + irpMinorString = IRP_MN_MDL_STRING; + break; + case IRP_MN_COMPLETE: + irpMinorString = IRP_MN_COMPLETE_STRING; + break; + case IRP_MN_COMPRESSED: + irpMinorString = IRP_MN_COMPRESSED_STRING; + break; + case IRP_MN_MDL_DPC: + irpMinorString = IRP_MN_MDL_DPC_STRING; + break; + case IRP_MN_COMPLETE_MDL: + irpMinorString = IRP_MN_COMPLETE_MDL_STRING; + break; + case IRP_MN_COMPLETE_MDL_DPC: + irpMinorString = IRP_MN_COMPLETE_MDL_DPC_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_QUERY_INFORMATION: + irpMajorString = IRP_MJ_QUERY_INFORMATION_STRING; + break; + case IRP_MJ_SET_INFORMATION: + irpMajorString = IRP_MJ_SET_INFORMATION_STRING; + break; + case IRP_MJ_QUERY_EA: + irpMajorString = IRP_MJ_QUERY_EA_STRING; + break; + case IRP_MJ_SET_EA: + irpMajorString = IRP_MJ_SET_EA_STRING; + break; + case IRP_MJ_FLUSH_BUFFERS: + irpMajorString = IRP_MJ_FLUSH_BUFFERS_STRING; + break; + case IRP_MJ_QUERY_VOLUME_INFORMATION: + irpMajorString = IRP_MJ_QUERY_VOLUME_INFORMATION_STRING; + break; + case IRP_MJ_SET_VOLUME_INFORMATION: + irpMajorString = IRP_MJ_SET_VOLUME_INFORMATION_STRING; + break; + case IRP_MJ_DIRECTORY_CONTROL: + irpMajorString = IRP_MJ_DIRECTORY_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_QUERY_DIRECTORY: + irpMinorString = IRP_MN_QUERY_DIRECTORY_STRING; + break; + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + irpMinorString = IRP_MN_NOTIFY_CHANGE_DIRECTORY_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_FILE_SYSTEM_CONTROL: + irpMajorString = IRP_MJ_FILE_SYSTEM_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_USER_FS_REQUEST: + irpMinorString = IRP_MN_USER_FS_REQUEST_STRING; + break; + case IRP_MN_MOUNT_VOLUME: + irpMinorString = IRP_MN_MOUNT_VOLUME_STRING; + break; + case IRP_MN_VERIFY_VOLUME: + irpMinorString = IRP_MN_VERIFY_VOLUME_STRING; + break; + case IRP_MN_LOAD_FILE_SYSTEM: + irpMinorString = IRP_MN_LOAD_FILE_SYSTEM_STRING; + break; + case IRP_MN_TRACK_LINK: + irpMinorString = IRP_MN_TRACK_LINK_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_DEVICE_CONTROL: + irpMajorString = IRP_MJ_DEVICE_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_SCSI_CLASS: + irpMinorString = IRP_MN_SCSI_CLASS_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_INTERNAL_DEVICE_CONTROL: + irpMajorString = IRP_MJ_INTERNAL_DEVICE_CONTROL_STRING; + break; + case IRP_MJ_SHUTDOWN: + irpMajorString = IRP_MJ_SHUTDOWN_STRING; + break; + case IRP_MJ_LOCK_CONTROL: + irpMajorString = IRP_MJ_LOCK_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_LOCK: + irpMinorString = IRP_MN_LOCK_STRING; + break; + case IRP_MN_UNLOCK_SINGLE: + irpMinorString = IRP_MN_UNLOCK_SINGLE_STRING; + break; + case IRP_MN_UNLOCK_ALL: + irpMinorString = IRP_MN_UNLOCK_ALL_STRING; + break; + case IRP_MN_UNLOCK_ALL_BY_KEY: + irpMinorString = IRP_MN_UNLOCK_ALL_BY_KEY_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_CLEANUP: + irpMajorString = IRP_MJ_CLEANUP_STRING; + break; + case IRP_MJ_CREATE_MAILSLOT: + irpMajorString = IRP_MJ_CREATE_MAILSLOT_STRING; + break; + case IRP_MJ_QUERY_SECURITY: + irpMajorString = IRP_MJ_QUERY_SECURITY_STRING; + break; + case IRP_MJ_SET_SECURITY: + irpMajorString = IRP_MJ_SET_SECURITY_STRING; + break; + case IRP_MJ_POWER: + irpMajorString = IRP_MJ_POWER_STRING; + switch (MinorCode) { + case IRP_MN_WAIT_WAKE: + irpMinorString = IRP_MN_WAIT_WAKE_STRING; + break; + case IRP_MN_POWER_SEQUENCE: + irpMinorString = IRP_MN_POWER_SEQUENCE_STRING; + break; + case IRP_MN_SET_POWER: + irpMinorString = IRP_MN_SET_POWER_STRING; + break; + case IRP_MN_QUERY_POWER: + irpMinorString = IRP_MN_QUERY_POWER_STRING; + break; + default : + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_SYSTEM_CONTROL: + irpMajorString = IRP_MJ_SYSTEM_CONTROL_STRING; + switch (MinorCode) { + case IRP_MN_QUERY_ALL_DATA: + irpMinorString = IRP_MN_QUERY_ALL_DATA_STRING; + break; + case IRP_MN_QUERY_SINGLE_INSTANCE: + irpMinorString = IRP_MN_QUERY_SINGLE_INSTANCE_STRING; + break; + case IRP_MN_CHANGE_SINGLE_INSTANCE: + irpMinorString = IRP_MN_CHANGE_SINGLE_INSTANCE_STRING; + break; + case IRP_MN_CHANGE_SINGLE_ITEM: + irpMinorString = IRP_MN_CHANGE_SINGLE_ITEM_STRING; + break; + case IRP_MN_ENABLE_EVENTS: + irpMinorString = IRP_MN_ENABLE_EVENTS_STRING; + break; + case IRP_MN_DISABLE_EVENTS: + irpMinorString = IRP_MN_DISABLE_EVENTS_STRING; + break; + case IRP_MN_ENABLE_COLLECTION: + irpMinorString = IRP_MN_ENABLE_COLLECTION_STRING; + break; + case IRP_MN_DISABLE_COLLECTION: + irpMinorString = IRP_MN_DISABLE_COLLECTION_STRING; + break; + case IRP_MN_REGINFO: + irpMinorString = IRP_MN_REGINFO_STRING; + break; + case IRP_MN_EXECUTE_METHOD: + irpMinorString = IRP_MN_EXECUTE_METHOD_STRING; + break; + default : + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + case IRP_MJ_DEVICE_CHANGE: + irpMajorString = IRP_MJ_DEVICE_CHANGE_STRING; + break; + case IRP_MJ_QUERY_QUOTA: + irpMajorString = IRP_MJ_QUERY_QUOTA_STRING; + break; + case IRP_MJ_SET_QUOTA: + irpMajorString = IRP_MJ_SET_QUOTA_STRING; + break; + case IRP_MJ_PNP: + irpMajorString = IRP_MJ_PNP_STRING; + switch (MinorCode) { + case IRP_MN_START_DEVICE: + irpMinorString = IRP_MN_START_DEVICE_STRING; + break; + case IRP_MN_QUERY_REMOVE_DEVICE: + irpMinorString = IRP_MN_QUERY_REMOVE_DEVICE_STRING; + break; + case IRP_MN_REMOVE_DEVICE: + irpMinorString = IRP_MN_REMOVE_DEVICE_STRING; + break; + case IRP_MN_CANCEL_REMOVE_DEVICE: + irpMinorString = IRP_MN_CANCEL_REMOVE_DEVICE_STRING; + break; + case IRP_MN_STOP_DEVICE: + irpMinorString = IRP_MN_STOP_DEVICE_STRING; + break; + case IRP_MN_QUERY_STOP_DEVICE: + irpMinorString = IRP_MN_QUERY_STOP_DEVICE_STRING; + break; + case IRP_MN_CANCEL_STOP_DEVICE: + irpMinorString = IRP_MN_CANCEL_STOP_DEVICE_STRING; + break; + case IRP_MN_QUERY_DEVICE_RELATIONS: + irpMinorString = IRP_MN_QUERY_DEVICE_RELATIONS_STRING; + break; + case IRP_MN_QUERY_INTERFACE: + irpMinorString = IRP_MN_QUERY_INTERFACE_STRING; + break; + case IRP_MN_QUERY_CAPABILITIES: + irpMinorString = IRP_MN_QUERY_CAPABILITIES_STRING; + break; + case IRP_MN_QUERY_RESOURCES: + irpMinorString = IRP_MN_QUERY_RESOURCES_STRING; + break; + case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: + irpMinorString = IRP_MN_QUERY_RESOURCE_REQUIREMENTS_STRING; + break; + case IRP_MN_QUERY_DEVICE_TEXT: + irpMinorString = IRP_MN_QUERY_DEVICE_TEXT_STRING; + break; + case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: + irpMinorString = IRP_MN_FILTER_RESOURCE_REQUIREMENTS_STRING; + break; + case IRP_MN_READ_CONFIG: + irpMinorString = IRP_MN_READ_CONFIG_STRING; + break; + case IRP_MN_WRITE_CONFIG: + irpMinorString = IRP_MN_WRITE_CONFIG_STRING; + break; + case IRP_MN_EJECT: + irpMinorString = IRP_MN_EJECT_STRING; + break; + case IRP_MN_SET_LOCK: + irpMinorString = IRP_MN_SET_LOCK_STRING; + break; + case IRP_MN_QUERY_ID: + irpMinorString = IRP_MN_QUERY_ID_STRING; + break; + case IRP_MN_QUERY_PNP_DEVICE_STATE: + irpMinorString = IRP_MN_QUERY_PNP_DEVICE_STATE_STRING; + break; + case IRP_MN_QUERY_BUS_INFORMATION: + irpMinorString = IRP_MN_QUERY_BUS_INFORMATION_STRING; + break; + case IRP_MN_DEVICE_USAGE_NOTIFICATION: + irpMinorString = IRP_MN_DEVICE_USAGE_NOTIFICATION_STRING; + break; + case IRP_MN_SURPRISE_REMOVAL: + irpMinorString = IRP_MN_SURPRISE_REMOVAL_STRING; + break; + case IRP_MN_QUERY_LEGACY_BUS_INFORMATION: + irpMinorString = IRP_MN_QUERY_LEGACY_BUS_INFORMATION_STRING; + break; + default : + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp minor code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + + case IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION: + irpMajorString = IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION_STRING; + break; + + case IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION: + irpMajorString = IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION_STRING; + break; + + case IRP_MJ_ACQUIRE_FOR_MOD_WRITE: + irpMajorString = IRP_MJ_ACQUIRE_FOR_MOD_WRITE_STRING; + break; + + case IRP_MJ_RELEASE_FOR_MOD_WRITE: + irpMajorString = IRP_MJ_RELEASE_FOR_MOD_WRITE_STRING; + break; + + case IRP_MJ_ACQUIRE_FOR_CC_FLUSH: + irpMajorString = IRP_MJ_ACQUIRE_FOR_CC_FLUSH_STRING; + break; + + case IRP_MJ_RELEASE_FOR_CC_FLUSH: + irpMajorString = IRP_MJ_RELEASE_FOR_CC_FLUSH_STRING; + break; + + case IRP_MJ_NOTIFY_STREAM_FO_CREATION: + irpMajorString = IRP_MJ_NOTIFY_STREAM_FO_CREATION_STRING; + break; + + + + case IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE: + irpMajorString = IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE_STRING; + break; + + case IRP_MJ_NETWORK_QUERY_OPEN: + irpMajorString = IRP_MJ_NETWORK_QUERY_OPEN_STRING; + break; + + case IRP_MJ_MDL_READ: + irpMajorString = IRP_MJ_MDL_READ_STRING; + break; + + case IRP_MJ_MDL_READ_COMPLETE: + irpMajorString = IRP_MJ_MDL_READ_COMPLETE_STRING; + break; + + case IRP_MJ_PREPARE_MDL_WRITE: + irpMajorString = IRP_MJ_PREPARE_MDL_WRITE_STRING; + break; + + case IRP_MJ_MDL_WRITE_COMPLETE: + irpMajorString = IRP_MJ_MDL_WRITE_COMPLETE_STRING; + break; + + case IRP_MJ_VOLUME_MOUNT: + irpMajorString = IRP_MJ_VOLUME_MOUNT_STRING; + break; + + case IRP_MJ_VOLUME_DISMOUNT: + irpMajorString = IRP_MJ_VOLUME_DISMOUNT_STRING; + break; + + case IRP_MJ_TRANSACTION_NOTIFY: + irpMajorString = IRP_MJ_TRANSACTION_NOTIFY_STRING; + switch (MinorCode) { + case 0: + irpMinorString = TRANSACTION_BEGIN; + break; + case TRANSACTION_NOTIFY_PREPREPARE_CODE: + irpMinorString = TRANSACTION_NOTIFY_PREPREPARE_STRING; + break; + case TRANSACTION_NOTIFY_PREPARE_CODE: + irpMinorString = TRANSACTION_NOTIFY_PREPARE_STRING; + break; + case TRANSACTION_NOTIFY_COMMIT_CODE: + irpMinorString = TRANSACTION_NOTIFY_COMMIT_STRING; + break; + case TRANSACTION_NOTIFY_COMMIT_FINALIZE_CODE: + irpMinorString = TRANSACTION_NOTIFY_COMMIT_FINALIZE_STRING; + break; + case TRANSACTION_NOTIFY_ROLLBACK_CODE: + irpMinorString = TRANSACTION_NOTIFY_ROLLBACK_STRING; + break; + case TRANSACTION_NOTIFY_PREPREPARE_COMPLETE_CODE: + irpMinorString = TRANSACTION_NOTIFY_PREPREPARE_COMPLETE_STRING; + break; + case TRANSACTION_NOTIFY_PREPARE_COMPLETE_CODE: + irpMinorString = TRANSACTION_NOTIFY_COMMIT_COMPLETE_STRING; + break; + case TRANSACTION_NOTIFY_ROLLBACK_COMPLETE_CODE: + irpMinorString = TRANSACTION_NOTIFY_ROLLBACK_COMPLETE_STRING; + break; + case TRANSACTION_NOTIFY_RECOVER_CODE: + irpMinorString = TRANSACTION_NOTIFY_RECOVER_STRING; + break; + case TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT_CODE: + irpMinorString = TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT_STRING; + break; + case TRANSACTION_NOTIFY_DELEGATE_COMMIT_CODE: + irpMinorString = TRANSACTION_NOTIFY_DELEGATE_COMMIT_STRING; + break; + case TRANSACTION_NOTIFY_RECOVER_QUERY_CODE: + irpMinorString = TRANSACTION_NOTIFY_RECOVER_QUERY_STRING; + break; + case TRANSACTION_NOTIFY_ENLIST_PREPREPARE_CODE: + irpMinorString = TRANSACTION_NOTIFY_ENLIST_PREPREPARE_STRING; + break; + case TRANSACTION_NOTIFY_LAST_RECOVER_CODE: + irpMinorString = TRANSACTION_NOTIFY_LAST_RECOVER_STRING; + break; + case TRANSACTION_NOTIFY_INDOUBT_CODE: + irpMinorString = TRANSACTION_NOTIFY_INDOUBT_STRING; + break; + case TRANSACTION_NOTIFY_PROPAGATE_PULL_CODE: + irpMinorString = TRANSACTION_NOTIFY_PROPAGATE_PULL_STRING; + break; + case TRANSACTION_NOTIFY_PROPAGATE_PUSH_CODE: + irpMinorString = TRANSACTION_NOTIFY_PROPAGATE_PUSH_STRING; + break; + case TRANSACTION_NOTIFY_MARSHAL_CODE: + irpMinorString = TRANSACTION_NOTIFY_MARSHAL_STRING; + break; + case TRANSACTION_NOTIFY_ENLIST_MASK_CODE: + irpMinorString = TRANSACTION_NOTIFY_ENLIST_MASK_STRING; + break; + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Transaction notication code (%u)",MinorCode); + irpMinorString = errorBuf; + } + break; + + + default: + sprintf_s(errorBuf,sizeof(errorBuf),"Unknown Irp major function (%d)",MajorCode); + irpMajorString = errorBuf; + break; + } + + if (OutputFile) { + + if (irpMinorString) { + + fprintf(OutputFile, "\t%-35s\t%-35s", irpMajorString, irpMinorString); + + } else { + + fprintf(OutputFile, "\t%-35s\t ", irpMajorString); + } + + } else { + + if (PrintMajorCode) { + + printf("%-35s ", irpMajorString); + + } else { + + if (irpMinorString) { + + printf(" %-35s\n", + irpMinorString); + } + } + } +} + + +ULONG +FormatSystemTime( + _In_ SYSTEMTIME *SystemTime, + _Out_writes_bytes_(BufferLength) CHAR *Buffer, + _In_ ULONG BufferLength + ) +/*++ +Routine Description: + + Formats the values in a SystemTime struct into the buffer + passed in. The resulting string is NULL terminated. The format + for the time is: + hours:minutes:seconds:milliseconds + +Arguments: + + SystemTime - the struct to format + Buffer - the buffer to place the formatted time in + BufferLength - the size of the buffer + +Return Value: + + The length of the string returned in Buffer. + +--*/ +{ + ULONG returnLength = 0; + + if (BufferLength < TIME_BUFFER_LENGTH) { + + // + // Buffer is too short so exit + // + + return 0; + } + + returnLength = sprintf_s( Buffer, + BufferLength, + "%02d:%02d:%02d:%03d", + SystemTime->wHour, + SystemTime->wMinute, + SystemTime->wSecond, + SystemTime->wMilliseconds ); + + return returnLength; +} + + +VOID +FileDump ( + _In_ ULONG SequenceNumber, + _In_ WCHAR CONST *Name, + _In_ PRECORD_DATA RecordData, + _In_ FILE *File + ) +/*++ +Routine Description: + + Prints a Data log record to the specified file. The output is in a tab + delimited format with the fields in the following order: + + SequenceNumber, OriginatingTime, CompletionTime, CallbackMajorId, CallbackMinorId, + Flags, NoCache, Paging I/O, Synchronous, Synchronous paging, FileName, + ReturnStatus, FileName + + +Arguments: + + SequenceNumber - the sequence number for this log record + Name - the name of the file that this Irp relates to + RecordData - the Data record to print + File - the file to print to + +Return Value: + + None. + +--*/ +{ + FILETIME localTime; + SYSTEMTIME systemTime; + CHAR time[TIME_BUFFER_LENGTH]; + static BOOLEAN didFileHeader = FALSE; + + // + // Is this an Irp or a FastIo? + // + + if (!didFileHeader) { + +#if defined(_WIN64) + fprintf( File, "Opr\t SeqNum \t PreOp Time \tPostOp Time \t Process.Thrd\t Major Operation \t Minor Operation \t IrpFlags \t DevObj \t FileObj \t Transactn \t status:inform \t Arg 1 \t Arg 2 \t Arg 3 \t Arg 4 \t Arg 5 \t Arg 6 \tName\n"); + fprintf( File, "---\t----------\t------------\t------------\t-------------\t-----------------------------------\t-----------------------------------\t---------------\t------------------\t------------------\t------------------\t-----------------------------\t------------------\t------------------\t------------------\t------------------\t------------------\t----------\t--------------------------------------------------\n"); +#else + fprintf( File, "Opr\t SeqNum \t PreOp Time \tPostOp Time \t Process.Thrd\t Major Operation \t Minor Operation \t IrpFlags \t DevObj \t FileObj \tTransactn \t status:inform \t Arg 1 \t Arg 2 \t Arg 3 \t Arg 4 \t Arg 5 \t Arg 6 \tName\n"); + fprintf( File, "---\t----------\t------------\t------------\t-------------\t-----------------------------------\t-----------------------------------\t---------------\t----------\t----------\t----------\t---------------------\t----------\t----------\t----------\t----------\t----------\t----------\t--------------------------------------------------\n"); +#endif + didFileHeader = TRUE; + } + + // + // Is this an Irp or a FastIo? + // + + if (RecordData->Flags & FLT_CALLBACK_DATA_IRP_OPERATION) { + + fprintf( File, "IRP"); + + } else if (RecordData->Flags & FLT_CALLBACK_DATA_FAST_IO_OPERATION) { + + fprintf( File, "FIO"); + + } else if (RecordData->Flags & FLT_CALLBACK_DATA_FS_FILTER_OPERATION) { + + fprintf( File, "FSF"); + + } else { + + fprintf( File, "ERR"); + } + + // + // Print the sequence number + // + + fprintf( File, "\t0x%08X", SequenceNumber ); + + // + // Convert originating time + // + + FileTimeToLocalFileTime( (FILETIME *)&(RecordData->OriginatingTime), + &localTime ); + FileTimeToSystemTime( &localTime, + &systemTime ); + + if (FormatSystemTime( &systemTime, time, TIME_BUFFER_LENGTH )) { + + fprintf( File, "\t%-12s", time ); + + } else { + + fprintf( File, "\t%-12s", TIME_ERROR ); + } + + // + // Convert completion time + // + + FileTimeToLocalFileTime( (FILETIME *)&(RecordData->CompletionTime), + &localTime ); + FileTimeToSystemTime( &localTime, + &systemTime ); + + if (FormatSystemTime( &systemTime, time, TIME_BUFFER_LENGTH )) { + + fprintf( File, "\t%-12s", time ); + + } else { + + fprintf( File, "\t%-12s", TIME_ERROR ); + } + + fprintf(File, "\t%8x.%-4x ", RecordData->ProcessId, RecordData->ThreadId); + + PrintIrpCode( RecordData->CallbackMajorId, + RecordData->CallbackMinorId, + File, + TRUE ); + + // + // Interpret set IrpFlags + // + + fprintf( File, "\t0x%08lx ", RecordData->IrpFlags ); + fprintf( File, "%s", (RecordData->IrpFlags & IRP_NOCACHE) ? "N":"-" ); + fprintf( File, "%s", (RecordData->IrpFlags & IRP_PAGING_IO) ? "P":"-" ); + fprintf( File, "%s", (RecordData->IrpFlags & IRP_SYNCHRONOUS_API) ? "S":"-" ); + fprintf( File, "%s", (RecordData->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) ? "Y":"-" ); + + fprintf( File, "\t0x%08p", (PVOID) RecordData->DeviceObject ); + fprintf( File, "\t0x%08p", (PVOID) RecordData->FileObject ); + fprintf( File, "\t0x%08p", (PVOID) RecordData->Transaction ); + fprintf( File, "\t0x%08lx:0x%p", RecordData->Status, (PVOID)RecordData->Information ); + + fprintf( File, "\t0x%p", RecordData->Arg1 ); + fprintf( File, "\t0x%p", RecordData->Arg2 ); + fprintf( File, "\t0x%p", RecordData->Arg3 ); + fprintf( File, "\t0x%p", RecordData->Arg4 ); + fprintf( File, "\t0x%p", RecordData->Arg5 ); + fprintf( File, "\t0x%08I64x", RecordData->Arg6.QuadPart ); + + fprintf( File, "\t%S", Name ); + fprintf( File, "\n" ); +} + + +VOID +ScreenDump( + _In_ ULONG SequenceNumber, + _In_ WCHAR CONST *Name, + _In_ PRECORD_DATA RecordData + ) +/*++ +Routine Description: + + Prints a Irp log record to the screen in the following order: + SequenceNumber, OriginatingTime, CompletionTime, IrpMajor, IrpMinor, + Flags, IrpFlags, NoCache, Paging I/O, Synchronous, Synchronous paging, + FileName, ReturnStatus, FileName + +Arguments: + + SequenceNumber - the sequence number for this log record + Name - the file name to which this Irp relates + RecordData - the Irp record to print + +Return Value: + + None. + +--*/ +{ + FILETIME localTime; + SYSTEMTIME systemTime; + CHAR time[TIME_BUFFER_LENGTH]; + static BOOLEAN didScreenHeader = FALSE; + + // + // Is this an Irp or a FastIo? + // + + if (!didScreenHeader) { + +#if defined(_WIN64) + printf("Opr SeqNum PreOp Time PostOp Time Process.Thrd Major/Minor Operation IrpFlags DevObj FileObj Transact status:inform Arguments Name\n"); + printf("--- -------- ------------ ------------ ------------- ----------------------------------- ------------- ---------------- ---------------- ---------------- ------------------------- --------------------------------------------------------------------------------------------------------- -----------------------------------\n"); +#else + printf("Opr SeqNum PreOp Time PostOp Time Process.Thrd Major/Minor Operation IrpFlags DevObj FileObj Transact status:inform Arguments Name\n"); + printf("--- -------- ------------ ------------ ------------- ----------------------------------- ------------- -------- -------- -------- ----------------- ----------------------------------------------------------------- -----------------------------------\n"); +#endif + didScreenHeader = TRUE; + } + + // + // Display informatoin + // + + if (RecordData->Flags & FLT_CALLBACK_DATA_IRP_OPERATION) { + + printf( "IRP "); + + } else if (RecordData->Flags & FLT_CALLBACK_DATA_FAST_IO_OPERATION) { + + printf( "FIO "); + + } else if (RecordData->Flags & FLT_CALLBACK_DATA_FS_FILTER_OPERATION) { + + printf( "FSF " ); + } else { + + printf( "ERR "); + } + + printf( "%08X ", SequenceNumber ); + + + // + // Convert originating time + // + + FileTimeToLocalFileTime( (FILETIME *)&(RecordData->OriginatingTime), + &localTime ); + FileTimeToSystemTime( &localTime, + &systemTime ); + + if (FormatSystemTime( &systemTime, time, TIME_BUFFER_LENGTH )) { + + printf( "%-12s ", time ); + + } else { + + printf( "%-12s ", TIME_ERROR ); + } + + // + // Convert completion time + // + + FileTimeToLocalFileTime( (FILETIME *)&(RecordData->CompletionTime), + &localTime ); + FileTimeToSystemTime( &localTime, + &systemTime ); + + if (FormatSystemTime( &systemTime, time, TIME_BUFFER_LENGTH )) { + + printf( "%-12s ", time ); + + } else { + + printf( "%-12s ", TIME_ERROR ); + } + + printf("%8x.%-4x ", RecordData->ProcessId, RecordData->ThreadId); + + PrintIrpCode( RecordData->CallbackMajorId, + RecordData->CallbackMinorId, + NULL, + TRUE ); + + // + // Interpret set IrpFlags + // + + printf( "%08lx ", RecordData->IrpFlags ); + printf( "%s", (RecordData->IrpFlags & IRP_NOCACHE) ? "N":"-" ); + printf( "%s", (RecordData->IrpFlags & IRP_PAGING_IO) ? "P":"-" ); + printf( "%s", (RecordData->IrpFlags & IRP_SYNCHRONOUS_API) ? "S":"-" ); + printf( "%s ", (RecordData->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) ? "Y":"-" ); + + printf( "%08p ", (PVOID) RecordData->DeviceObject ); + printf( "%08p ", (PVOID) RecordData->FileObject ); + printf( "%08p ", (PVOID) RecordData->Transaction ); + printf( "%08lx:%p ", RecordData->Status, (PVOID)RecordData->Information ); + + printf( "1:%p 2:%p 3:%p 4:%p 5:%p 6:%08I64x ", + RecordData->Arg1, + RecordData->Arg2, + RecordData->Arg3, + RecordData->Arg4, + RecordData->Arg5, + RecordData->Arg6.QuadPart ); + + printf( "%S", Name ); + printf( "\n" ); + PrintIrpCode( RecordData->CallbackMajorId, + RecordData->CallbackMinorId, + NULL, + FALSE ); +} + diff --git a/filesys/miniFilter/minispy/user/mspyLog.h b/filesys/miniFilter/minispy/user/mspyLog.h new file mode 100644 index 00000000..00377800 --- /dev/null +++ b/filesys/miniFilter/minispy/user/mspyLog.h @@ -0,0 +1,425 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyLog.h + +Abstract: + + This module contains the structures and prototypes used by the user + program to retrieve and see the log records recorded by MiniSpy.sys. + +Environment: + + User mode + +--*/ +#ifndef __MSPYLOG_H__ +#define __MSPYLOG_H__ + +#include <stdio.h> +#include <fltUser.h> +#include "minispy.h" + +#define BUFFER_SIZE 4096 + +// +// Structure for managing current state. +// + +typedef struct _LOG_CONTEXT { + + HANDLE Port; + BOOLEAN LogToScreen; + BOOLEAN LogToFile; + FILE *OutputFile; + + BOOLEAN NextLogToScreen; + + // + // For synchronizing shutting down of both threads + // + + BOOLEAN CleaningUp; + HANDLE ShutDown; + +} LOG_CONTEXT, *PLOG_CONTEXT; + +// +// Function prototypes +// + +DWORD WINAPI +RetrieveLogRecords( + _In_ LPVOID lpParameter + ); + +VOID +FileDump ( + _In_ ULONG SequenceNumber, + _In_ WCHAR CONST *Name, + _In_ PRECORD_DATA RecordData, + _In_ FILE *File + ); + +VOID +ScreenDump( + _In_ ULONG SequenceNumber, + _In_ WCHAR CONST *Name, + _In_ PRECORD_DATA RecordData + ); + +// +// Values set for the Flags field in a RECORD_DATA structure. +// These flags come from the FLT_CALLBACK_DATA structure. +// + +#define FLT_CALLBACK_DATA_IRP_OPERATION 0x00000001 // Set for Irp operations +#define FLT_CALLBACK_DATA_FAST_IO_OPERATION 0x00000002 // Set for Fast Io operations +#define FLT_CALLBACK_DATA_FS_FILTER_OPERATION 0x00000004 // Set for FsFilter operations + +// +// standard IRP_MJ string definitions +// + +#define IRP_MJ_CREATE_STRING "IRP_MJ_CREATE" +#define IRP_MJ_CREATE_NAMED_PIPE_STRING "IRP_MJ_CREATE_NAMED_PIPE" +#define IRP_MJ_CLOSE_STRING "IRP_MJ_CLOSE" +#define IRP_MJ_READ_STRING "IRP_MJ_READ" +#define IRP_MJ_WRITE_STRING "IRP_MJ_WRITE" +#define IRP_MJ_QUERY_INFORMATION_STRING "IRP_MJ_QUERY_INFORMATION" +#define IRP_MJ_SET_INFORMATION_STRING "IRP_MJ_SET_INFORMATION" +#define IRP_MJ_QUERY_EA_STRING "IRP_MJ_QUERY_EA" +#define IRP_MJ_SET_EA_STRING "IRP_MJ_SET_EA" +#define IRP_MJ_FLUSH_BUFFERS_STRING "IRP_MJ_FLUSH_BUFFERS" +#define IRP_MJ_QUERY_VOLUME_INFORMATION_STRING "IRP_MJ_QUERY_VOLUME_INFORMATION" +#define IRP_MJ_SET_VOLUME_INFORMATION_STRING "IRP_MJ_SET_VOLUME_INFORMATION" +#define IRP_MJ_DIRECTORY_CONTROL_STRING "IRP_MJ_DIRECTORY_CONTROL" +#define IRP_MJ_FILE_SYSTEM_CONTROL_STRING "IRP_MJ_FILE_SYSTEM_CONTROL" +#define IRP_MJ_DEVICE_CONTROL_STRING "IRP_MJ_DEVICE_CONTROL" +#define IRP_MJ_INTERNAL_DEVICE_CONTROL_STRING "IRP_MJ_INTERNAL_DEVICE_CONTROL" +#define IRP_MJ_SHUTDOWN_STRING "IRP_MJ_SHUTDOWN" +#define IRP_MJ_LOCK_CONTROL_STRING "IRP_MJ_LOCK_CONTROL" +#define IRP_MJ_CLEANUP_STRING "IRP_MJ_CLEANUP" +#define IRP_MJ_CREATE_MAILSLOT_STRING "IRP_MJ_CREATE_MAILSLOT" +#define IRP_MJ_QUERY_SECURITY_STRING "IRP_MJ_QUERY_SECURITY" +#define IRP_MJ_SET_SECURITY_STRING "IRP_MJ_SET_SECURITY" +#define IRP_MJ_POWER_STRING "IRP_MJ_POWER" +#define IRP_MJ_SYSTEM_CONTROL_STRING "IRP_MJ_SYSTEM_CONTROL" +#define IRP_MJ_DEVICE_CHANGE_STRING "IRP_MJ_DEVICE_CHANGE" +#define IRP_MJ_QUERY_QUOTA_STRING "IRP_MJ_QUERY_QUOTA" +#define IRP_MJ_SET_QUOTA_STRING "IRP_MJ_SET_QUOTA" +#define IRP_MJ_PNP_STRING "IRP_MJ_PNP" +#define IRP_MJ_MAXIMUM_FUNCTION_STRING "IRP_MJ_MAXIMUM_FUNCTION" + +// +// FSFilter string definitions +// + +#define IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION_STRING "IRP_MJ_ACQUIRE_FOR_SECTION_SYNC" +#define IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION_STRING "IRP_MJ_RELEASE_FOR_SECTION_SYNC" +#define IRP_MJ_ACQUIRE_FOR_MOD_WRITE_STRING "IRP_MJ_ACQUIRE_FOR_MOD_WRITE" +#define IRP_MJ_RELEASE_FOR_MOD_WRITE_STRING "IRP_MJ_RELEASE_FOR_MOD_WRITE" +#define IRP_MJ_ACQUIRE_FOR_CC_FLUSH_STRING "IRP_MJ_ACQUIRE_FOR_CC_FLUSH" +#define IRP_MJ_RELEASE_FOR_CC_FLUSH_STRING "IRP_MJ_RELEASE_FOR_CC_FLUSH" +#define IRP_MJ_NOTIFY_STREAM_FO_CREATION_STRING "IRP_MJ_NOTIFY_STREAM_FO_CREATION" + +// +// FAST_IO and other string definitions +// + +#define IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE_STRING "IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE" +#define IRP_MJ_DETACH_DEVICE_STRING "IRP_MJ_DETACH_DEVICE" +#define IRP_MJ_NETWORK_QUERY_OPEN_STRING "IRP_MJ_NETWORK_QUERY_OPEN" +#define IRP_MJ_MDL_READ_STRING "IRP_MJ_MDL_READ" +#define IRP_MJ_MDL_READ_COMPLETE_STRING "IRP_MJ_MDL_READ_COMPLETE" +#define IRP_MJ_PREPARE_MDL_WRITE_STRING "IRP_MJ_PREPARE_MDL_WRITE" +#define IRP_MJ_MDL_WRITE_COMPLETE_STRING "IRP_MJ_MDL_WRITE_COMPLETE" +#define IRP_MJ_VOLUME_MOUNT_STRING "IRP_MJ_VOLUME_MOUNT" +#define IRP_MJ_VOLUME_DISMOUNT_STRING "IRP_MJ_VOLUME_DISMOUNT" + +// +// Strings for the Irp minor codes +// + +#define IRP_MN_QUERY_DIRECTORY_STRING "IRP_MN_QUERY_DIRECTORY" +#define IRP_MN_NOTIFY_CHANGE_DIRECTORY_STRING "IRP_MN_NOTIFY_CHANGE_DIRECTORY" +#define IRP_MN_USER_FS_REQUEST_STRING "IRP_MN_USER_FS_REQUEST" +#define IRP_MN_MOUNT_VOLUME_STRING "IRP_MN_MOUNT_VOLUME" +#define IRP_MN_VERIFY_VOLUME_STRING "IRP_MN_VERIFY_VOLUME" +#define IRP_MN_LOAD_FILE_SYSTEM_STRING "IRP_MN_LOAD_FILE_SYSTEM" +#define IRP_MN_TRACK_LINK_STRING "IRP_MN_TRACK_LINK" +#define IRP_MN_LOCK_STRING "IRP_MN_LOCK" +#define IRP_MN_UNLOCK_SINGLE_STRING "IRP_MN_UNLOCK_SINGLE" +#define IRP_MN_UNLOCK_ALL_STRING "IRP_MN_UNLOCK_ALL" +#define IRP_MN_UNLOCK_ALL_BY_KEY_STRING "IRP_MN_UNLOCK_ALL_BY_KEY" +#define IRP_MN_NORMAL_STRING "IRP_MN_NORMAL" +#define IRP_MN_DPC_STRING "IRP_MN_DPC" +#define IRP_MN_MDL_STRING "IRP_MN_MDL" +#define IRP_MN_COMPLETE_STRING "IRP_MN_COMPLETE" +#define IRP_MN_COMPRESSED_STRING "IRP_MN_COMPRESSED" +#define IRP_MN_MDL_DPC_STRING "IRP_MN_MDL_DPC" +#define IRP_MN_COMPLETE_MDL_STRING "IRP_MN_COMPLETE_MDL" +#define IRP_MN_COMPLETE_MDL_DPC_STRING "IRP_MN_COMPLETE_MDL_DPC" +#define IRP_MN_SCSI_CLASS_STRING "IRP_MN_SCSI_CLASS" +#define IRP_MN_START_DEVICE_STRING "IRP_MN_START_DEVICE" +#define IRP_MN_QUERY_REMOVE_DEVICE_STRING "IRP_MN_QUERY_REMOVE_DEVICE" +#define IRP_MN_REMOVE_DEVICE_STRING "IRP_MN_REMOVE_DEVICE" +#define IRP_MN_CANCEL_REMOVE_DEVICE_STRING "IRP_MN_CANCEL_REMOVE_DEVICE" +#define IRP_MN_STOP_DEVICE_STRING "IRP_MN_STOP_DEVICE" +#define IRP_MN_QUERY_STOP_DEVICE_STRING "IRP_MN_QUERY_STOP_DEVICE" +#define IRP_MN_CANCEL_STOP_DEVICE_STRING "IRP_MN_CANCEL_STOP_DEVICE" +#define IRP_MN_QUERY_DEVICE_RELATIONS_STRING "IRP_MN_QUERY_DEVICE_RELATIONS" +#define IRP_MN_QUERY_INTERFACE_STRING "IRP_MN_QUERY_INTERFACE" +#define IRP_MN_QUERY_CAPABILITIES_STRING "IRP_MN_QUERY_CAPABILITIES" +#define IRP_MN_QUERY_RESOURCES_STRING "IRP_MN_QUERY_RESOURCES" +#define IRP_MN_QUERY_RESOURCE_REQUIREMENTS_STRING "IRP_MN_QUERY_RESOURCE_REQUIREMENTS" +#define IRP_MN_QUERY_DEVICE_TEXT_STRING "IRP_MN_QUERY_DEVICE_TEXT" +#define IRP_MN_FILTER_RESOURCE_REQUIREMENTS_STRING "IRP_MN_FILTER_RESOURCE_REQUIREMENTS" +#define IRP_MN_READ_CONFIG_STRING "IRP_MN_READ_CONFIG" +#define IRP_MN_WRITE_CONFIG_STRING "IRP_MN_WRITE_CONFIG" +#define IRP_MN_EJECT_STRING "IRP_MN_EJECT" +#define IRP_MN_SET_LOCK_STRING "IRP_MN_SET_LOCK" +#define IRP_MN_QUERY_ID_STRING "IRP_MN_QUERY_ID" +#define IRP_MN_QUERY_PNP_DEVICE_STATE_STRING "IRP_MN_QUERY_PNP_DEVICE_STATE" +#define IRP_MN_QUERY_BUS_INFORMATION_STRING "IRP_MN_QUERY_BUS_INFORMATION" +#define IRP_MN_DEVICE_USAGE_NOTIFICATION_STRING "IRP_MN_DEVICE_USAGE_NOTIFICATION" +#define IRP_MN_SURPRISE_REMOVAL_STRING "IRP_MN_SURPRISE_REMOVAL" +#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION_STRING "IRP_MN_QUERY_LEGACY_BUS_INFORMATION" +#define IRP_MN_WAIT_WAKE_STRING "IRP_MN_WAIT_WAKE" +#define IRP_MN_POWER_SEQUENCE_STRING "IRP_MN_POWER_SEQUENCE" +#define IRP_MN_SET_POWER_STRING "IRP_MN_SET_POWER" +#define IRP_MN_QUERY_POWER_STRING "IRP_MN_QUERY_POWER" +#define IRP_MN_QUERY_ALL_DATA_STRING "IRP_MN_QUERY_ALL_DATA" +#define IRP_MN_QUERY_SINGLE_INSTANCE_STRING "IRP_MN_QUERY_SINGLE_INSTANCE" +#define IRP_MN_CHANGE_SINGLE_INSTANCE_STRING "IRP_MN_CHANGE_SINGLE_INSTANCE" +#define IRP_MN_CHANGE_SINGLE_ITEM_STRING "IRP_MN_CHANGE_SINGLE_ITEM" +#define IRP_MN_ENABLE_EVENTS_STRING "IRP_MN_ENABLE_EVENTS" +#define IRP_MN_DISABLE_EVENTS_STRING "IRP_MN_DISABLE_EVENTS" +#define IRP_MN_ENABLE_COLLECTION_STRING "IRP_MN_ENABLE_COLLECTION" +#define IRP_MN_DISABLE_COLLECTION_STRING "IRP_MN_DISABLE_COLLECTION" +#define IRP_MN_REGINFO_STRING "IRP_MN_REGINFO" +#define IRP_MN_EXECUTE_METHOD_STRING "IRP_MN_EXECUTE_METHOD" + +// +// Transaction notification string definitions. +// + +#define IRP_MJ_TRANSACTION_NOTIFY_STRING "IRP_MJ_TRANSACTION_NOTIFY" + +#define TRANSACTION_BEGIN "BEGIN_TRANSACTION" +#define TRANSACTION_NOTIFY_PREPREPARE_STRING "TRANSACTION_NOTIFY_PREPREPARE" +#define TRANSACTION_NOTIFY_PREPARE_STRING "TRANSACTION_NOTIFY_PREPARE" +#define TRANSACTION_NOTIFY_COMMIT_STRING "TRANSACTION_NOTIFY_COMMIT" +#define TRANSACTION_NOTIFY_ROLLBACK_STRING "TRANSACTION_NOTIFY_ROLLBACK" +#define TRANSACTION_NOTIFY_PREPREPARE_COMPLETE_STRING "TRANSACTION_NOTIFY_PREPREPARE_COMPLETE" +#define TRANSACTION_NOTIFY_PREPARE_COMPLETE_STRING "TRANSACTION_NOTIFY_PREPARE_COMPLETE" +#define TRANSACTION_NOTIFY_COMMIT_COMPLETE_STRING "TRANSACTION_NOTIFY_COMMIT_COMPLETE" +#define TRANSACTION_NOTIFY_COMMIT_FINALIZE_STRING "TRANSACTION_NOTIFY_COMMIT_FINALIZE" +#define TRANSACTION_NOTIFY_ROLLBACK_COMPLETE_STRING "TRANSACTION_NOTIFY_ROLLBACK_COMPLETE" +#define TRANSACTION_NOTIFY_RECOVER_STRING "TRANSACTION_NOTIFY_RECOVER" +#define TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT_STRING "TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT" +#define TRANSACTION_NOTIFY_DELEGATE_COMMIT_STRING "TRANSACTION_NOTIFY_DELEGATE_COMMIT" +#define TRANSACTION_NOTIFY_RECOVER_QUERY_STRING "TRANSACTION_NOTIFY_RECOVER_QUERY" +#define TRANSACTION_NOTIFY_ENLIST_PREPREPARE_STRING "TRANSACTION_NOTIFY_ENLIST_PREPREPARE" +#define TRANSACTION_NOTIFY_LAST_RECOVER_STRING "TRANSACTION_NOTIFY_LAST_RECOVER" +#define TRANSACTION_NOTIFY_INDOUBT_STRING "TRANSACTION_NOTIFY_INDOUBT" +#define TRANSACTION_NOTIFY_PROPAGATE_PULL_STRING "TRANSACTION_NOTIFY_PROPAGATE_PULL" +#define TRANSACTION_NOTIFY_PROPAGATE_PUSH_STRING "TRANSACTION_NOTIFY_PROPAGATE_PUSH" +#define TRANSACTION_NOTIFY_MARSHAL_STRING "TRANSACTION_NOTIFY_MARSHAL" +#define TRANSACTION_NOTIFY_ENLIST_MASK_STRING "TRANSACTION_NOTIFY_ENLIST_MASK" + + +// +// FltMgr's IRP major codes +// + +#define IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION ((UCHAR)-1) +#define IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION ((UCHAR)-2) +#define IRP_MJ_ACQUIRE_FOR_MOD_WRITE ((UCHAR)-3) +#define IRP_MJ_RELEASE_FOR_MOD_WRITE ((UCHAR)-4) +#define IRP_MJ_ACQUIRE_FOR_CC_FLUSH ((UCHAR)-5) +#define IRP_MJ_RELEASE_FOR_CC_FLUSH ((UCHAR)-6) +#define IRP_MJ_NOTIFY_STREAM_FO_CREATION ((UCHAR)-7) + +#define IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE ((UCHAR)-13) +#define IRP_MJ_NETWORK_QUERY_OPEN ((UCHAR)-14) +#define IRP_MJ_MDL_READ ((UCHAR)-15) +#define IRP_MJ_MDL_READ_COMPLETE ((UCHAR)-16) +#define IRP_MJ_PREPARE_MDL_WRITE ((UCHAR)-17) +#define IRP_MJ_MDL_WRITE_COMPLETE ((UCHAR)-18) +#define IRP_MJ_VOLUME_MOUNT ((UCHAR)-19) +#define IRP_MJ_VOLUME_DISMOUNT ((UCHAR)-20) + + +typedef enum { + TRANSACTION_NOTIFY_PREPREPARE_CODE = 1, + TRANSACTION_NOTIFY_PREPARE_CODE, + TRANSACTION_NOTIFY_COMMIT_CODE, + TRANSACTION_NOTIFY_ROLLBACK_CODE, + TRANSACTION_NOTIFY_PREPREPARE_COMPLETE_CODE, + TRANSACTION_NOTIFY_PREPARE_COMPLETE_CODE, + TRANSACTION_NOTIFY_COMMIT_COMPLETE_CODE, + TRANSACTION_NOTIFY_ROLLBACK_COMPLETE_CODE, + TRANSACTION_NOTIFY_RECOVER_CODE, + TRANSACTION_NOTIFY_SINGLE_PHASE_COMMIT_CODE, + TRANSACTION_NOTIFY_DELEGATE_COMMIT_CODE, + TRANSACTION_NOTIFY_RECOVER_QUERY_CODE, + TRANSACTION_NOTIFY_ENLIST_PREPREPARE_CODE, + TRANSACTION_NOTIFY_LAST_RECOVER_CODE, + TRANSACTION_NOTIFY_INDOUBT_CODE, + TRANSACTION_NOTIFY_PROPAGATE_PULL_CODE, + TRANSACTION_NOTIFY_PROPAGATE_PUSH_CODE, + TRANSACTION_NOTIFY_MARSHAL_CODE, + TRANSACTION_NOTIFY_ENLIST_MASK_CODE, + TRANSACTION_NOTIFY_COMMIT_FINALIZE_CODE = 31 +} TRANSACTION_NOTIFICATION_CODES; + +// +// Standard IRP Major codes +// + +#define IRP_MJ_CREATE 0x00 +#define IRP_MJ_CREATE_NAMED_PIPE 0x01 +#define IRP_MJ_CLOSE 0x02 +#define IRP_MJ_READ 0x03 +#define IRP_MJ_WRITE 0x04 +#define IRP_MJ_QUERY_INFORMATION 0x05 +#define IRP_MJ_SET_INFORMATION 0x06 +#define IRP_MJ_QUERY_EA 0x07 +#define IRP_MJ_SET_EA 0x08 +#define IRP_MJ_FLUSH_BUFFERS 0x09 +#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a +#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b +#define IRP_MJ_DIRECTORY_CONTROL 0x0c +#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d +#define IRP_MJ_DEVICE_CONTROL 0x0e +#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f +#define IRP_MJ_SHUTDOWN 0x10 +#define IRP_MJ_LOCK_CONTROL 0x11 +#define IRP_MJ_CLEANUP 0x12 +#define IRP_MJ_CREATE_MAILSLOT 0x13 +#define IRP_MJ_QUERY_SECURITY 0x14 +#define IRP_MJ_SET_SECURITY 0x15 +#define IRP_MJ_POWER 0x16 +#define IRP_MJ_SYSTEM_CONTROL 0x17 +#define IRP_MJ_DEVICE_CHANGE 0x18 +#define IRP_MJ_QUERY_QUOTA 0x19 +#define IRP_MJ_SET_QUOTA 0x1a +#define IRP_MJ_PNP 0x1b +#define IRP_MJ_MAXIMUM_FUNCTION 0x1b + +// +// IRP minor codes +// + +#define IRP_MN_QUERY_DIRECTORY 0x01 +#define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x02 +#define IRP_MN_USER_FS_REQUEST 0x00 +#define IRP_MN_MOUNT_VOLUME 0x01 +#define IRP_MN_VERIFY_VOLUME 0x02 +#define IRP_MN_LOAD_FILE_SYSTEM 0x03 +#define IRP_MN_TRACK_LINK 0x04 +#define IRP_MN_LOCK 0x01 +#define IRP_MN_UNLOCK_SINGLE 0x02 +#define IRP_MN_UNLOCK_ALL 0x03 +#define IRP_MN_UNLOCK_ALL_BY_KEY 0x04 +#define IRP_MN_NORMAL 0x00 +#define IRP_MN_DPC 0x01 +#define IRP_MN_MDL 0x02 +#define IRP_MN_COMPLETE 0x04 +#define IRP_MN_COMPRESSED 0x08 +#define IRP_MN_MDL_DPC (IRP_MN_MDL | IRP_MN_DPC) +#define IRP_MN_COMPLETE_MDL (IRP_MN_COMPLETE | IRP_MN_MDL) +#define IRP_MN_COMPLETE_MDL_DPC (IRP_MN_COMPLETE_MDL | IRP_MN_DPC) +#define IRP_MN_SCSI_CLASS 0x01 +#define IRP_MN_START_DEVICE 0x00 +#define IRP_MN_QUERY_REMOVE_DEVICE 0x01 +#define IRP_MN_REMOVE_DEVICE 0x02 +#define IRP_MN_CANCEL_REMOVE_DEVICE 0x03 +#define IRP_MN_STOP_DEVICE 0x04 +#define IRP_MN_QUERY_STOP_DEVICE 0x05 +#define IRP_MN_CANCEL_STOP_DEVICE 0x06 +#define IRP_MN_QUERY_DEVICE_RELATIONS 0x07 +#define IRP_MN_QUERY_INTERFACE 0x08 +#define IRP_MN_QUERY_CAPABILITIES 0x09 +#define IRP_MN_QUERY_RESOURCES 0x0A +#define IRP_MN_QUERY_RESOURCE_REQUIREMENTS 0x0B +#define IRP_MN_QUERY_DEVICE_TEXT 0x0C +#define IRP_MN_FILTER_RESOURCE_REQUIREMENTS 0x0D +#define IRP_MN_READ_CONFIG 0x0F +#define IRP_MN_WRITE_CONFIG 0x10 +#define IRP_MN_EJECT 0x11 +#define IRP_MN_SET_LOCK 0x12 +#define IRP_MN_QUERY_ID 0x13 +#define IRP_MN_QUERY_PNP_DEVICE_STATE 0x14 +#define IRP_MN_QUERY_BUS_INFORMATION 0x15 +#define IRP_MN_DEVICE_USAGE_NOTIFICATION 0x16 +#define IRP_MN_SURPRISE_REMOVAL 0x17 +#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18 +#define IRP_MN_WAIT_WAKE 0x00 +#define IRP_MN_POWER_SEQUENCE 0x01 +#define IRP_MN_SET_POWER 0x02 +#define IRP_MN_QUERY_POWER 0x03 +#define IRP_MN_QUERY_ALL_DATA 0x00 +#define IRP_MN_QUERY_SINGLE_INSTANCE 0x01 +#define IRP_MN_CHANGE_SINGLE_INSTANCE 0x02 +#define IRP_MN_CHANGE_SINGLE_ITEM 0x03 +#define IRP_MN_ENABLE_EVENTS 0x04 +#define IRP_MN_DISABLE_EVENTS 0x05 +#define IRP_MN_ENABLE_COLLECTION 0x06 +#define IRP_MN_DISABLE_COLLECTION 0x07 +#define IRP_MN_REGINFO 0x08 +#define IRP_MN_EXECUTE_METHOD 0x09 + +// +// IRP Flags +// + +#define IRP_NOCACHE 0x00000001 +#define IRP_PAGING_IO 0x00000002 +#define IRP_SYNCHRONOUS_API 0x00000004 +#define IRP_SYNCHRONOUS_PAGING_IO 0x00000040 + +// +// Define the FLT_TAG_DATA structure so that we can display it. +// + +#pragma warning(push) +#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union + +typedef struct _FLT_TAG_DATA_BUFFER { + ULONG FileTag; + USHORT TagDataLength; + USHORT UnparsedNameLength; + union { + GUID TagGuid; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} FLT_TAG_DATA_BUFFER, *PFLT_TAG_DATA_BUFFER; + +#pragma warning(pop) + +#endif //__MSPYLOG_H__ + diff --git a/filesys/miniFilter/minispy/user/mspyUser.c b/filesys/miniFilter/minispy/user/mspyUser.c new file mode 100644 index 00000000..e5f5e2f5 --- /dev/null +++ b/filesys/miniFilter/minispy/user/mspyUser.c @@ -0,0 +1,979 @@ +/*++ + +Copyright (c) 1989-2002 Microsoft Corporation + +Module Name: + + mspyUser.c + +Abstract: + + This file contains the implementation for the main function of the + user application piece of MiniSpy. This function is responsible for + controlling the command mode available to the user to control the + kernel mode driver. + +Environment: + + User mode + +--*/ + +#include <DriverSpecs.h> +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include <stdlib.h> +#include <stdio.h> +#include <windows.h> +#include <assert.h> +#include "mspyLog.h" +#include <strsafe.h> + +#define SUCCESS 0 +#define USAGE_ERROR 1 +#define EXIT_INTERPRETER 2 +#define EXIT_PROGRAM 4 + +#define INTERPRETER_EXIT_COMMAND1 "go" +#define INTERPRETER_EXIT_COMMAND2 "g" +#define PROGRAM_EXIT_COMMAND "exit" +#define CMDLINE_SIZE 256 +#define NUM_PARAMS 40 + +#define MINISPY_NAME L"MiniSpy" + +DWORD +InterpretCommand ( + _In_ int argc, + _In_reads_(argc) char *argv[], + _In_ PLOG_CONTEXT Context + ); + +VOID +ListDevices ( + VOID + ); + +VOID +DisplayError ( + _In_ DWORD Code + ) + +/*++ + +Routine Description: + + This routine will display an error message based off of the Win32 error + code that is passed in. This allows the user to see an understandable + error message instead of just the code. + +Arguments: + + Code - The error code to be translated. + +Return Value: + + None. + +--*/ + +{ + WCHAR buffer[MAX_PATH] = { 0 }; + DWORD count; + HMODULE module = NULL; + HRESULT status; + + count = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + Code, + 0, + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + + + if (count == 0) { + + count = GetSystemDirectory( buffer, + sizeof(buffer) / sizeof( WCHAR ) ); + + if (count==0 || count > sizeof(buffer) / sizeof( WCHAR )) { + + // + // In practice we expect buffer to be large enough to hold the + // system directory path. + // + + printf(" Could not translate error: %d\n", Code); + return; + } + + + status = StringCchCat( buffer, + sizeof(buffer) / sizeof( WCHAR ), + L"\\fltlib.dll" ); + + if (status != S_OK) { + + printf(" Could not translate error: %d\n", Code); + return; + } + + module = LoadLibraryExW( buffer, NULL, LOAD_LIBRARY_AS_DATAFILE ); + + // + // Translate the Win32 error code into a useful message. + // + + count = FormatMessage (FORMAT_MESSAGE_FROM_HMODULE, + module, + Code, + 0, + buffer, + sizeof(buffer) / sizeof(WCHAR), + NULL); + + if (module != NULL) { + + FreeLibrary( module ); + } + + // + // If we still couldn't resolve the message, generate a string + // + + if (count == 0) { + + printf(" Could not translate error: %d\n", Code); + return; + } + } + + // + // Display the translated error. + // + + printf(" %ws\n", buffer); +} + +// +// Main uses a loop which has an assignment in the while +// conditional statement. Suppress the compiler's warning. +// + +#pragma warning(push) +#pragma warning(disable:4706) // assignment within conditional expression + +int _cdecl +main ( + _In_ int argc, + _In_reads_(argc) char *argv[] + ) +/*++ + +Routine Description: + + Main routine for minispy + +Arguments: + +Return Value: + +--*/ +{ + HANDLE port = INVALID_HANDLE_VALUE; + HRESULT hResult = S_OK; + DWORD result; + ULONG threadId; + HANDLE thread = NULL; + LOG_CONTEXT context; + CHAR inputChar; + + // + // Initialize handle in case of error + // + + context.ShutDown = NULL; + + // + // Open the port that is used to talk to + // MiniSpy. + // + + printf( "Connecting to filter's port...\n" ); + + hResult = FilterConnectCommunicationPort( MINISPY_PORT_NAME, + 0, + NULL, + 0, + NULL, + &port ); + + if (IS_ERROR( hResult )) { + + printf( "Could not connect to filter: 0x%08x\n", hResult ); + DisplayError( hResult ); + goto Main_Exit; + } + + // + // Initialize the fields of the LOG_CONTEXT + // + + context.Port = port; + context.ShutDown = CreateSemaphore( NULL, + 0, + 1, + L"MiniSpy shut down" ); + context.CleaningUp = FALSE; + context.LogToFile = FALSE; + context.LogToScreen = FALSE; //don't start logging yet + context.NextLogToScreen = TRUE; + context.OutputFile = NULL; + + if (context.ShutDown == NULL) { + + result = GetLastError(); + printf( "Could not create semaphore: %d\n", result ); + DisplayError( result ); + goto Main_Exit; + } + + // + // Check the valid parameters for startup + // + + if (argc > 1) { + + if (InterpretCommand( argc - 1, &(argv[1]), &context ) == USAGE_ERROR) { + + goto Main_Exit; + } + } + + // + // Create the thread to read the log records that are gathered + // by MiniSpy.sys. + // + printf( "Creating logging thread...\n" ); + thread = CreateThread( NULL, + 0, + RetrieveLogRecords, + (LPVOID)&context, + 0, + &threadId); + + if (!thread) { + + result = GetLastError(); + printf( "Could not create logging thread: %d\n", result ); + DisplayError( result ); + goto Main_Exit; + } + + // + // Check to see what devices we are attached to from + // previous runs of this program. + // + + ListDevices(); + + // + // Process commands from the user + // + + printf( "\nHit [Enter] to begin command mode...\n\n" ); + fflush( stdout ); + + // + // set screen logging state + // + + context.LogToScreen = context.NextLogToScreen; + + while (inputChar = (CHAR)getchar()) { + + CHAR *parms[NUM_PARAMS]; + CHAR commandLine[CMDLINE_SIZE+1]; + INT parmCount, count; + DWORD returnValue = SUCCESS; + BOOL newParm; + CHAR ch; + + if (inputChar == '\n') { + + // + // Start command interpreter. First we must turn off logging + // to screen if we are. Also, remember the state of logging + // to the screen, so that we can reinstate that when command + // interpreter is finished. + // + + context.NextLogToScreen = context.LogToScreen; + context.LogToScreen = FALSE; + + while (returnValue != EXIT_INTERPRETER) { + + // + // Print prompt + // + printf( ">" ); + + // + // Read in next line, keeping track of the number of parameters + // as we go. + // + + parmCount = 0; + newParm = TRUE; + for ( count = 0; + (count < CMDLINE_SIZE) && ((ch = (CHAR)getchar()) != '\n'); + count++) + { + commandLine[count] = ch; + + if (newParm && (ch != ' ')) { + + parms[parmCount++] = &commandLine[count]; + } + + if (parmCount >= NUM_PARAMS) { + + break; + } + + // + // Always insert NULL's for spaces + // + + if (ch == ' ') { + + newParm = TRUE; + commandLine[count] = 0; + + } else { + + newParm = FALSE; + } + } + + commandLine[count] = '\0'; + + if (parmCount == 0) { + + continue; + } + + // + // We've got our parameter count and parameter list, so + // send it off to be interpreted. + // + + returnValue = InterpretCommand( parmCount, parms, &context ); + + if (returnValue == EXIT_PROGRAM) { + + // Time to stop the program + goto Main_Cleanup; + } + } + + // + // Set LogToScreen appropriately based on any commands seen + // + + context.LogToScreen = context.NextLogToScreen; + + if (context.LogToScreen) { + + printf( "Should be logging to screen...\n" ); + } + } + } + +Main_Cleanup: + + // + // Clean up the threads, then fall through to Main_Exit + // + + printf( "Cleaning up...\n" ); + + // + // Set the Cleaning up flag to TRUE to notify other threads + // that we are cleaning up + // + context.CleaningUp = TRUE; + + // + // Wait for everyone to shut down + // + + WaitForSingleObject( context.ShutDown, INFINITE ); + + if (context.LogToFile) { + + fclose( context.OutputFile ); + } + +Main_Exit: + + // + // Clean up the data that is always around and exit + // + + if(context.ShutDown) { + + CloseHandle( context.ShutDown ); + } + + if (thread) { + + CloseHandle( thread ); + } + + if (INVALID_HANDLE_VALUE != port) { + CloseHandle( port ); + } + return 0; +} + +#pragma warning(pop) + +DWORD +InterpretCommand ( + _In_ int argc, + _In_reads_(argc) char *argv[], + _In_ PLOG_CONTEXT Context + ) +/*++ + +Routine Description: + + Process options from the user + +Arguments: + +Return Value: + +--*/ +{ + LONG parmIndex; + PCHAR parm; + HRESULT hResult; + DWORD returnValue = SUCCESS; + CHAR buffer[BUFFER_SIZE]; + DWORD bufferLength; + PWCHAR instanceString; + WCHAR instanceName[INSTANCE_NAME_MAX_CHARS + 1]; + + // + // Interpret the command line parameters + // + for (parmIndex = 0; parmIndex < argc; parmIndex++) { + + parm = argv[parmIndex]; + + if (parm[0] == '/') { + + // + // Have the beginning of a switch + // + + switch (parm[1]) { + + case 'a': + case 'A': + + // + // Attach to the specified drive letter + // + + parmIndex++; + + if (parmIndex >= argc) { + + // + // Not enough parameters + // + + goto InterpretCommand_Usage; + } + + parm = argv[parmIndex]; + + printf( " Attaching to %s... ", parm ); + + bufferLength = MultiByteToWideChar( CP_ACP, + MB_ERR_INVALID_CHARS, + parm, + -1, + (LPWSTR)buffer, + BUFFER_SIZE/sizeof( WCHAR ) ); + + if (bufferLength == 0) { + + // + // We do not expect the user to provide a parm that + // causes buffer to overflow. + // + + goto InterpretCommand_Usage; + } + + hResult = FilterAttach( MINISPY_NAME, + (PWSTR)buffer, + NULL, // instance name + sizeof( instanceName ), + instanceName ); + + if (SUCCEEDED( hResult )) { + + printf( " Instance name: %S\n", instanceName ); + + } else { + + printf( "\n Could not attach to device: 0x%08x\n", hResult ); + DisplayError( hResult ); + returnValue = SUCCESS; + } + + break; + + case 'd': + case 'D': + + // + // Detach to the specified drive letter + // + + parmIndex++; + + if (parmIndex >= argc) { + + // + // Not enough parameters + // + + goto InterpretCommand_Usage; + } + + parm = argv[parmIndex]; + + printf( " Detaching from %s\n", parm ); + bufferLength = MultiByteToWideChar( CP_ACP, + MB_ERR_INVALID_CHARS, + parm, + -1, + (LPWSTR)buffer, + BUFFER_SIZE/sizeof( WCHAR ) ); + + if (bufferLength == 0) { + + // + // We do not expect the user to provide a parm that + // causes buffer to overflow. + // + + goto InterpretCommand_Usage; + } + + // + // Get the next argument to see if it is an InstanceId + // + + parmIndex++; + + if (parmIndex >= argc) { + + instanceString = NULL; + + } else { + + if (argv[parmIndex][0] == '/') { + + // + // This is just the next command, so don't + // internet it as the InstanceId. + // + + instanceString = NULL; + parmIndex--; + + } else { + + parm = argv[parmIndex]; + bufferLength = MultiByteToWideChar( CP_ACP, + MB_ERR_INVALID_CHARS, + parm, + -1, + (LPWSTR)instanceName, + sizeof( instanceName )/sizeof( WCHAR ) ); + + if (bufferLength == 0) { + + // + // We do not expect the user to provide a parm that + // causes buffer to overflow. + // + + goto InterpretCommand_Usage; + } + + instanceString = instanceName; + } + } + + // + // Detach from the volume and instance specified. + // + + hResult = FilterDetach( MINISPY_NAME, + (PWSTR)buffer, + instanceString ); + + if (IS_ERROR( hResult )) { + + printf( " Could not detach from device: 0x%08x\n", hResult ); + DisplayError( hResult ); + returnValue = SUCCESS; + } + break; + + case 'l': + case 'L': + + // + // List all devices that are currently being monitored + // + + ListDevices(); + break; + + case 's': + case 'S': + + // + // Output logging results to screen, save new value to + // instate when command interpreter is exited. + // + if (Context->NextLogToScreen) { + + printf( " Turning off logging to screen\n" ); + + } else { + + printf( " Turning on logging to screen\n" ); + } + + Context->NextLogToScreen = !Context->NextLogToScreen; + break; + + case 'f': + case 'F': + + // + // Output logging results to file + // + + if (Context->LogToFile) { + + printf( " Stop logging to file \n" ); + Context->LogToFile = FALSE; + assert( Context->OutputFile ); + _Analysis_assume_( Context->OutputFile != NULL ); + fclose( Context->OutputFile ); + Context->OutputFile = NULL; + + } else { + + parmIndex++; + + if (parmIndex >= argc) { + + // + // Not enough parameters + // + + goto InterpretCommand_Usage; + } + + parm = argv[parmIndex]; + printf( " Log to file %s\n", parm ); + + if (fopen_s( &Context->OutputFile, parm, "w" ) != 0 ) { + assert( Context->OutputFile ); + } + + Context->LogToFile = TRUE; + } + break; + + default: + + // + // Invalid switch, goto usage + // + goto InterpretCommand_Usage; + } + + } else { + + // + // Look for "go" or "g" to see if we should exit interpreter + // + + if (!_strnicmp( parm, + INTERPRETER_EXIT_COMMAND1, + sizeof( INTERPRETER_EXIT_COMMAND1 ))) { + + returnValue = EXIT_INTERPRETER; + goto InterpretCommand_Exit; + } + + if (!_strnicmp( parm, + INTERPRETER_EXIT_COMMAND2, + sizeof( INTERPRETER_EXIT_COMMAND2 ))) { + + returnValue = EXIT_INTERPRETER; + goto InterpretCommand_Exit; + } + + // + // Look for "exit" to see if we should exit program + // + + if (!_strnicmp( parm, + PROGRAM_EXIT_COMMAND, + sizeof( PROGRAM_EXIT_COMMAND ))) { + + returnValue = EXIT_PROGRAM; + goto InterpretCommand_Exit; + } + + // + // Invalid parameter + // + goto InterpretCommand_Usage; + } + } + +InterpretCommand_Exit: + return returnValue; + +InterpretCommand_Usage: + printf("Valid switches: [/a <drive>] [/d <drive>] [/l] [/s] [/f [<file name>]]\n" + " [/a <drive>] starts monitoring <drive>\n" + " [/d <drive> [<instance id>]] detaches filter <instance id> from <drive>\n" + " [/l] lists all the drives the monitor is currently attached to\n" + " [/s] turns on and off showing logging output on the screen\n" + " [/f [<file name>]] turns on and off logging to the specified file\n" + " If you are in command mode:\n" + " [enter] will enter command mode\n" + " [go|g] will exit command mode\n" + " [exit] will terminate this program\n" + ); + returnValue = USAGE_ERROR; + goto InterpretCommand_Exit; +} + + +ULONG +IsAttachedToVolume( + _In_ LPCWSTR VolumeName + ) +/*++ + +Routine Description: + + Determine if our filter is attached to this volume + +Arguments: + + VolumeName - The volume we are checking + +Return Value: + + TRUE - we are attached + FALSE - we are not attached (or we couldn't tell) + +--*/ +{ + PWCHAR filtername; + CHAR buffer[1024]; + PINSTANCE_FULL_INFORMATION data = (PINSTANCE_FULL_INFORMATION)buffer; + HANDLE volumeIterator = INVALID_HANDLE_VALUE; + ULONG bytesReturned; + ULONG instanceCount = 0; + HRESULT hResult; + + // + // Enumerate all instances on this volume + // + + hResult = FilterVolumeInstanceFindFirst( VolumeName, + InstanceFullInformation, + data, + sizeof(buffer)-sizeof(WCHAR), + &bytesReturned, + &volumeIterator ); + + if (IS_ERROR( hResult )) { + + return instanceCount; + } + + do { + + assert((data->FilterNameBufferOffset+data->FilterNameLength) <= (sizeof(buffer)-sizeof(WCHAR))); + _Analysis_assume_((data->FilterNameBufferOffset+data->FilterNameLength) <= (sizeof(buffer)-sizeof(WCHAR))); + + // + // Get the name. Note that we are NULL terminating the buffer + // in place. We can do this because we don't care about the other + // information and we have guaranteed that there is room for a NULL + // at the end of the buffer. + // + + + filtername = Add2Ptr(data,data->FilterNameBufferOffset); + filtername[data->FilterNameLength/sizeof( WCHAR )] = L'\0'; + + // + // Bump the instance count when we find a match + // + + if (_wcsicmp(filtername,MINISPY_NAME) == 0) { + + instanceCount++; + } + + } while (SUCCEEDED( FilterVolumeInstanceFindNext( volumeIterator, + InstanceFullInformation, + data, + sizeof(buffer)-sizeof(WCHAR), + &bytesReturned ) )); + + // + // Close the handle + // + + FilterVolumeInstanceFindClose( volumeIterator ); + return instanceCount; +} + + +void +ListDevices( + VOID + ) +/*++ + +Routine Description: + + Display the volumes we are attached to + +Arguments: + +Return Value: + +--*/ +{ + UCHAR buffer[1024]; + PFILTER_VOLUME_BASIC_INFORMATION volumeBuffer = (PFILTER_VOLUME_BASIC_INFORMATION)buffer; + HANDLE volumeIterator = INVALID_HANDLE_VALUE; + ULONG volumeBytesReturned; + HRESULT hResult = S_OK; + WCHAR driveLetter[15] = { 0 }; + ULONG instanceCount; + + try { + + // + // Find out size of buffer needed + // + + hResult = FilterVolumeFindFirst( FilterVolumeBasicInformation, + volumeBuffer, + sizeof(buffer)-sizeof(WCHAR), //save space to null terminate name + &volumeBytesReturned, + &volumeIterator ); + + if (IS_ERROR( hResult )) { + + leave; + } + + assert( INVALID_HANDLE_VALUE != volumeIterator ); + + // + // Output the header + // + + printf( "\n" + "Dos Name Volume Name Status \n" + "-------------- ------------------------------------ --------\n" ); + + // + // Loop through all of the filters, displaying instance information + // + + do { + + assert((FIELD_OFFSET(FILTER_VOLUME_BASIC_INFORMATION,FilterVolumeName) + volumeBuffer->FilterVolumeNameLength) <= (sizeof(buffer)-sizeof(WCHAR))); + _Analysis_assume_((FIELD_OFFSET(FILTER_VOLUME_BASIC_INFORMATION,FilterVolumeName) + volumeBuffer->FilterVolumeNameLength) <= (sizeof(buffer)-sizeof(WCHAR))); + + volumeBuffer->FilterVolumeName[volumeBuffer->FilterVolumeNameLength/sizeof( WCHAR )] = UNICODE_NULL; + + instanceCount = IsAttachedToVolume(volumeBuffer->FilterVolumeName); + + printf( "%-14ws %-36ws %s", + (SUCCEEDED( FilterGetDosName( + volumeBuffer->FilterVolumeName, + driveLetter, + sizeof(driveLetter)/sizeof(WCHAR) )) ? driveLetter : L""), + volumeBuffer->FilterVolumeName, + (instanceCount > 0) ? "Attached" : ""); + + if (instanceCount > 1) { + + printf( " (%d)\n", instanceCount ); + + } else { + + printf( "\n" ); + } + + } while (SUCCEEDED( hResult = FilterVolumeFindNext( volumeIterator, + FilterVolumeBasicInformation, + volumeBuffer, + sizeof(buffer)-sizeof(WCHAR), //save space to null terminate name + &volumeBytesReturned ) )); + + if (HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) == hResult) { + + hResult = S_OK; + } + + } finally { + + if (INVALID_HANDLE_VALUE != volumeIterator) { + + FilterVolumeFindClose( volumeIterator ); + } + + if (IS_ERROR( hResult )) { + + if (HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) == hResult) { + + printf( "No volumes found.\n" ); + + } else { + + printf( "Volume listing failed with error: 0x%08x\n", + hResult ); + } + } + } +} + diff --git a/filesys/miniFilter/minispy/user/mspyUser.rc b/filesys/miniFilter/minispy/user/mspyUser.rc new file mode 100644 index 00000000..3fcef6be --- /dev/null +++ b/filesys/miniFilter/minispy/user/mspyUser.rc @@ -0,0 +1,10 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "MiniSpy Control Program" +#define VER_INTERNALNAME_STR "MiniSpy.exe" +#define VER_ORIGINALFILENAME_STR "MiniSpy.exe" + +#include "common.ver" diff --git a/filesys/miniFilter/nullFilter/ReadMe.md b/filesys/miniFilter/nullFilter/ReadMe.md new file mode 100644 index 00000000..376618e7 --- /dev/null +++ b/filesys/miniFilter/nullFilter/ReadMe.md @@ -0,0 +1,15 @@ +NullFilter File System Minifilter Driver +======================================== + +The NullFilter minifilter is a sample minifilter that shows how to register a minifilter with the filter manager. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *NullFilter* minifilter is a simple minifilter that registers itself with the filter manager for no callback operations. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/nullFilter/nullFilter.c b/filesys/miniFilter/nullFilter/nullFilter.c new file mode 100644 index 00000000..9944db13 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.c @@ -0,0 +1,240 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + nullFilter.c + +Abstract: + + This is the main module of the nullFilter mini filter driver. + It is a simple minifilter that registers itself with the main filter + for no callback operations. + +Environment: + + Kernel mode + +--*/ + +#include <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +//--------------------------------------------------------------------------- +// Global variables +//--------------------------------------------------------------------------- + +#define NULL_FILTER_FILTER_NAME L"NullFilter" + +typedef struct _NULL_FILTER_DATA { + + // + // The filter handle that results from a call to + // FltRegisterFilter. + // + + PFLT_FILTER FilterHandle; + +} NULL_FILTER_DATA, *PNULL_FILTER_DATA; + + +/************************************************************************* + Prototypes for the startup and unload routines used for + this Filter. + + Implementation in nullFilter.c +*************************************************************************/ + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +NullUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +NullQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +// +// Structure that contains all the global data structures +// used throughout NullFilter. +// + +NULL_FILTER_DATA NullFilterData; + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, NullUnload) +#pragma alloc_text(PAGE, NullQueryTeardown) +#endif + + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + NULL, // Context + NULL, // Operation callbacks + + NullUnload, // FilterUnload + + NULL, // InstanceSetup + NullQueryTeardown, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent + +}; + + +/************************************************************************* + Filter initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers the miniFilter with FltMgr 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; + + UNREFERENCED_PARAMETER( RegistryPath ); + + // + // Register with FltMgr + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &NullFilterData.FilterHandle ); + + FLT_ASSERT( NT_SUCCESS( status ) ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering i/o + // + + status = FltStartFiltering( NullFilterData.FilterHandle ); + + if (!NT_SUCCESS( status )) { + FltUnregisterFilter( NullFilterData.FilterHandle ); + } + } + return status; +} + +NTSTATUS +NullUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter 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(); + + FltUnregisterFilter( NullFilterData.FilterHandle ); + + return STATUS_SUCCESS; +} + +NTSTATUS +NullQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the instance detach routine for this miniFilter driver. + 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(); + + return STATUS_SUCCESS; +} + diff --git a/filesys/miniFilter/nullFilter/nullFilter.inf b/filesys/miniFilter/nullFilter/nullFilter.inf new file mode 100644 index 00000000..e1cb7db1 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.inf @@ -0,0 +1,95 @@ +;;; +;;; NullFilter +;;; +;;; +;;; Copyright (c) 1999 - 2002, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.0 +CatalogFile = nullfilter.cat + + +[DestinationDirs] +DefaultDestDir = 12 +NullFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = NullFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,NullFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = NullFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[NullFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = NullFilter.AddRegistry + +; +; Registry Modifications +; + +[NullFilter.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[NullFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +nullfilter.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "NullFilter mini-filter driver" +ServiceName = "NullFilter" +DriverName = "NullFilter" +DiskId1 = "NullFilter Device Installation Disk" + +;Instances specific information. +DefaultInstance = "Null Instance" +Instance1.Name = "Null Instance" +Instance1.Altitude = "370020" +Instance1.Flags = 0x1 ; Suppress automatic attachments diff --git a/filesys/miniFilter/nullFilter/nullFilter.rc b/filesys/miniFilter/nullFilter/nullFilter.rc new file mode 100644 index 00000000..79355bf3 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Null filter" +#define VER_INTERNALNAME_STR "nullFilter.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/nullFilter/nullFilter.sln b/filesys/miniFilter/nullFilter/nullFilter.sln new file mode 100644 index 00000000..9af52d43 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nullFilter", "nullFilter.vcxproj", "{83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|Win32.ActiveCfg = Debug|Win32 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|Win32.Build.0 = Debug|Win32 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|Win32.ActiveCfg = Release|Win32 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|Win32.Build.0 = Release|Win32 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|x64.ActiveCfg = Debug|x64 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Debug|x64.Build.0 = Debug|x64 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|x64.ActiveCfg = Release|x64 + {83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/nullFilter/nullFilter.vcxproj b/filesys/miniFilter/nullFilter/nullFilter.vcxproj new file mode 100644 index 00000000..7e24749f --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.vcxproj @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{83F8E913-4ED0-4CB2-ACF2-5411AC436CC5}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{6431C66E-F38F-45F1-BE1E-D06503CE7799}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>nullFilter</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>nullFilter</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>nullFilter</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>nullFilter</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="nullFilter.c" /> + <ResourceCompile Include="nullFilter.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters b/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters new file mode 100644 index 00000000..a199c829 --- /dev/null +++ b/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{AC14DB33-6BC1-44EC-AAB1-816BA496A388}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{F9EA13FF-7409-4FCD-B877-4F877D4E24BF}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{365A22DE-D53C-4640-8237-5E79E158A537}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{8F416348-B401-4C11-9DD0-4D79812667AD}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="nullFilter.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="nullFilter.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/passThrough/ReadMe.md b/filesys/miniFilter/passThrough/ReadMe.md new file mode 100644 index 00000000..cccc85de --- /dev/null +++ b/filesys/miniFilter/passThrough/ReadMe.md @@ -0,0 +1,15 @@ +PassThrough File System Minifilter Driver +========================================= + +The PassThrough minifilter demonstrates how to specify callback functions for different types of I/O requests. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *PassThrough* minifilter does not have any real functionality. For each type of I/O operation, the same pre and post callback functions are called. These callback functions simply forward the I/O request to the next filter on the stack. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/passThrough/passThrough.c b/filesys/miniFilter/passThrough/passThrough.c new file mode 100644 index 00000000..584fbe37 --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.c @@ -0,0 +1,887 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + passThrough.c + +Abstract: + + This is the main module of the passThrough miniFilter driver. + This filter hooks all IO operations for both pre and post operation + callbacks. The filter passes through the operations. + +Environment: + + Kernel mode + +--*/ + +#include <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + + +PFLT_FILTER gFilterHandle; +ULONG_PTR OperationStatusCtx = 1; + +#define PTDBG_TRACE_ROUTINES 0x00000001 +#define PTDBG_TRACE_OPERATION_STATUS 0x00000002 + +ULONG gTraceFlags = 0; + + +#define PT_DBG_PRINT( _dbgLevel, _string ) \ + (FlagOn(gTraceFlags,(_dbgLevel)) ? \ + DbgPrint _string : \ + ((int)0)) + +/************************************************************************* + Prototypes +*************************************************************************/ + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +PtInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +PtInstanceTeardownStart ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +VOID +PtInstanceTeardownComplete ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags + ); + +NTSTATUS +PtUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +PtInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +PtPreOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +VOID +PtOperationStatusCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_IO_PARAMETER_BLOCK ParameterSnapshot, + _In_ NTSTATUS OperationStatus, + _In_ PVOID RequesterContext + ); + +FLT_POSTOP_CALLBACK_STATUS +PtPostOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +PtPreOperationNoPostOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +BOOLEAN +PtDoRequestOperationStatus( + _In_ PFLT_CALLBACK_DATA Data + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, PtUnload) +#pragma alloc_text(PAGE, PtInstanceQueryTeardown) +#pragma alloc_text(PAGE, PtInstanceSetup) +#pragma alloc_text(PAGE, PtInstanceTeardownStart) +#pragma alloc_text(PAGE, PtInstanceTeardownComplete) +#endif + +// +// operation registration +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_CREATE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_CREATE_NAMED_PIPE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_CLOSE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_READ, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_WRITE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_INFORMATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_INFORMATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_EA, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_EA, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_FLUSH_BUFFERS, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_VOLUME_INFORMATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_VOLUME_INFORMATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_DIRECTORY_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_DEVICE_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_INTERNAL_DEVICE_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SHUTDOWN, + 0, + PtPreOperationNoPostOperationPassThrough, + NULL }, //post operations not supported + + { IRP_MJ_LOCK_CONTROL, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_CLEANUP, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_CREATE_MAILSLOT, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_SECURITY, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_SECURITY, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_QUERY_QUOTA, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_SET_QUOTA, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_PNP, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_ACQUIRE_FOR_MOD_WRITE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_RELEASE_FOR_MOD_WRITE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_ACQUIRE_FOR_CC_FLUSH, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_RELEASE_FOR_CC_FLUSH, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_MDL_READ, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_MDL_READ_COMPLETE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_PREPARE_MDL_WRITE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_MDL_WRITE_COMPLETE, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_VOLUME_MOUNT, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_VOLUME_DISMOUNT, + 0, + PtPreOperationPassThrough, + PtPostOperationPassThrough }, + + { IRP_MJ_OPERATION_END } +}; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + NULL, // Context + Callbacks, // Operation callbacks + + PtUnload, // MiniFilterUnload + + PtInstanceSetup, // InstanceSetup + PtInstanceQueryTeardown, // InstanceQueryTeardown + PtInstanceTeardownStart, // InstanceTeardownStart + PtInstanceTeardownComplete, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent + +}; + + + +NTSTATUS +PtInstanceSetup ( + _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. + + If this routine is not defined in the registration structure, automatic + instances are alwasys created. + +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. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtInstanceSetup: Entered\n") ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +PtInstanceQueryTeardown ( + _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. + + If this routine is not defined in the registration structure, explicit + detach requests via FltDetachVolume or FilterDetach will always be + failed. + +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(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtInstanceQueryTeardown: Entered\n") ); + + return STATUS_SUCCESS; +} + + +VOID +PtInstanceTeardownStart ( + _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. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtInstanceTeardownStart: Entered\n") ); +} + + +VOID +PtInstanceTeardownComplete ( + _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 ); + + PAGED_CODE(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtInstanceTeardownComplete: Entered\n") ); +} + + +/************************************************************************* + MiniFilter initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for this miniFilter driver. This + registers with FltMgr and initializes all 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; + + UNREFERENCED_PARAMETER( RegistryPath ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!DriverEntry: Entered\n") ); + + // + // Register with FltMgr to tell it our callback routines + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &gFilterHandle ); + + FLT_ASSERT( NT_SUCCESS( status ) ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering i/o + // + + status = FltStartFiltering( gFilterHandle ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( gFilterHandle ); + } + } + + return status; +} + +NTSTATUS +PtUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for this miniFilter 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(); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtUnload: Entered\n") ); + + FltUnregisterFilter( gFilterHandle ); + + return STATUS_SUCCESS; +} + + +/************************************************************************* + MiniFilter callback routines. +*************************************************************************/ +FLT_PREOP_CALLBACK_STATUS +PtPreOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the main pre-operation dispatch routine for this + miniFilter. Since this is just a simple passThrough miniFilter it + does not do anything with the callbackData but rather return + FLT_PREOP_SUCCESS_WITH_CALLBACK thereby passing it down to the next + miniFilter in the chain. + + This is non-pageable because it could be called on the paging path + +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. + +--*/ +{ + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtPreOperationPassThrough: Entered\n") ); + + // + // See if this is an operation we would like the operation status + // for. If so request it. + // + // NOTE: most filters do NOT need to do this. You only need to make + // this call if, for example, you need to know if the oplock was + // actually granted. + // + + if (PtDoRequestOperationStatus( Data )) { + + status = FltRequestOperationStatusCallback( Data, + PtOperationStatusCallback, + (PVOID)(++OperationStatusCtx) ); + if (!NT_SUCCESS(status)) { + + PT_DBG_PRINT( PTDBG_TRACE_OPERATION_STATUS, + ("PassThrough!PtPreOperationPassThrough: FltRequestOperationStatusCallback Failed, status=%08x\n", + status) ); + } + } + + return FLT_PREOP_SUCCESS_WITH_CALLBACK; +} + + + +VOID +PtOperationStatusCallback ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PFLT_IO_PARAMETER_BLOCK ParameterSnapshot, + _In_ NTSTATUS OperationStatus, + _In_ PVOID RequesterContext + ) +/*++ + +Routine Description: + + This routine is called when the given operation returns from the call + to IoCallDriver. This is useful for operations where STATUS_PENDING + means the operation was successfully queued. This is useful for OpLocks + and directory change notification operations. + + This callback is called in the context of the originating thread and will + never be called at DPC level. The file object has been correctly + referenced so that you can access it. It will be automatically + dereferenced upon return. + + This is non-pageable because it could be called on the paging path + +Arguments: + + FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing + opaque handles to this filter, instance, its associated volume and + file object. + + RequesterContext - The context for the completion routine for this + operation. + + OperationStatus - + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtOperationStatusCallback: Entered\n") ); + + PT_DBG_PRINT( PTDBG_TRACE_OPERATION_STATUS, + ("PassThrough!PtOperationStatusCallback: Status=%08x ctx=%p IrpMj=%02x.%02x \"%s\"\n", + OperationStatus, + RequesterContext, + ParameterSnapshot->MajorFunction, + ParameterSnapshot->MinorFunction, + FltGetIrpName(ParameterSnapshot->MajorFunction)) ); +} + + +FLT_POSTOP_CALLBACK_STATUS +PtPostOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine is the post-operation completion routine for this + miniFilter. + + This is non-pageable because it may be called at DPC level. + +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 completion context set in the pre-operation routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + The return value is the status of the operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtPostOperationPassThrough: Entered\n") ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +PtPreOperationNoPostOperationPassThrough ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine is the main pre-operation dispatch routine for this + miniFilter. Since this is just a simple passThrough miniFilter it + does not do anything with the callbackData but rather return + FLT_PREOP_SUCCESS_WITH_CALLBACK thereby passing it down to the next + miniFilter in the chain. + + This is non-pageable because it could be called on the paging path + +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. + +--*/ +{ + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PT_DBG_PRINT( PTDBG_TRACE_ROUTINES, + ("PassThrough!PtPreOperationNoPostOperationPassThrough: Entered\n") ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +BOOLEAN +PtDoRequestOperationStatus( + _In_ PFLT_CALLBACK_DATA Data + ) +/*++ + +Routine Description: + + This identifies those operations we want the operation status for. These + are typically operations that return STATUS_PENDING as a normal completion + status. + +Arguments: + +Return Value: + + TRUE - If we want the operation status + FALSE - If we don't + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + + // + // return boolean state based on which operations we are interested in + // + + return (BOOLEAN) + + // + // Check for oplock operations + // + + (((iopb->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && + ((iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) || + (iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) || + (iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_1) || + (iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2))) + + || + + // + // Check for directy change notification + // + + ((iopb->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) && + (iopb->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)) + ); +} + diff --git a/filesys/miniFilter/passThrough/passThrough.inf b/filesys/miniFilter/passThrough/passThrough.inf new file mode 100644 index 00000000..28eb9b4a --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.inf @@ -0,0 +1,96 @@ +;;; +;;; PassThrough +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.1 +CatalogFile = passthrough.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"DebugFlags",0x00010001 ,0x0 +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +passthrough.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "PassThrough Mini-Filter Driver" +ServiceName = "PassThrough" +DriverName = "PassThrough" +DiskId1 = "PassThrough Device Installation Disk" + +;Instances specific information. +DefaultInstance = "PassThrough Instance" +Instance1.Name = "PassThrough Instance" +Instance1.Altitude = "370030" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/passThrough/passThrough.rc b/filesys/miniFilter/passThrough/passThrough.rc new file mode 100644 index 00000000..5c6fdc46 --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "PassThrough Filter Driver" +#define VER_INTERNALNAME_STR "passThrough.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/passThrough/passThrough.sln b/filesys/miniFilter/passThrough/passThrough.sln new file mode 100644 index 00000000..26698330 --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passThrough", "passThrough.vcxproj", "{6925DFEC-4D62-4106-9465-F4BFC1CA280B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|Win32.ActiveCfg = Debug|Win32 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|Win32.Build.0 = Debug|Win32 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|Win32.ActiveCfg = Release|Win32 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|Win32.Build.0 = Release|Win32 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|x64.ActiveCfg = Debug|x64 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Debug|x64.Build.0 = Debug|x64 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|x64.ActiveCfg = Release|x64 + {6925DFEC-4D62-4106-9465-F4BFC1CA280B}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/passThrough/passThrough.vcxproj b/filesys/miniFilter/passThrough/passThrough.vcxproj new file mode 100644 index 00000000..66521b00 --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.vcxproj @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{6925DFEC-4D62-4106-9465-F4BFC1CA280B}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{D0990CEE-D681-43CD-9797-60AB0D628853}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>passThrough</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>passThrough</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>passThrough</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>passThrough</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="passThrough.c" /> + <ResourceCompile Include="passThrough.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters b/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters new file mode 100644 index 00000000..24e2fc4c --- /dev/null +++ b/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{D8FC9FC6-EBA8-4607-B388-0F5F1CACBA9C}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{968D5F4E-042F-4EB1-89CA-8A4DC5FAD3B8}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{BD7D5A5F-EEB5-43CF-AEF9-602F292252B7}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{6C7310BB-F3EA-4F55-A624-111FE5C6DF8D}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="passThrough.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="passThrough.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/scanner/ReadMe.md b/filesys/miniFilter/scanner/ReadMe.md new file mode 100644 index 00000000..979b2c25 --- /dev/null +++ b/filesys/miniFilter/scanner/ReadMe.md @@ -0,0 +1,17 @@ +Scanner File System Minifilter Driver +===================================== + +The Scanner minifilter is an example for developers who intend to write filters that examine data in files. Typically, antivirus products fall into this category. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The Scanner minifilter comprises both kernel-mode and user-mode components. The kernel-mode component recognizes appropriate moments for scanning a file's data and passes it to the user-mode component for further validation. The user-mode component creates a number of threads that await validation requests and corresponding data from the kernel-mode component. After scanning the data for occurrences of a "foul" string, the user-mode component sends an appropriate response to the kernel-mode component. + +The kernel-mode component scans files with specific extensions only. The file is first scanned on a successful open. If the file was opened with write access, it is scanned again before a close. Scanning is also performed on data that is about to be written to a file. Writes will be rejected if any occurrences of a "foul" string are found in the data. If a "foul" string is detected during the closing of a file, a debug message is printed. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/scanner/filter/scanner.c b/filesys/miniFilter/scanner/filter/scanner.c new file mode 100644 index 00000000..cd72d71b --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.c @@ -0,0 +1,1685 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scanner.c + +Abstract: + + This is the main module of the scanner filter. + + This filter scans the data in a file before allowing an open to proceed. This is similar + to what virus checkers do. + +Environment: + + Kernel mode + +--*/ + +#include <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> +#include "scanuk.h" +#include "scanner.h" + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + +#define SCANNER_REG_TAG 'Rncs' +#define SCANNER_STRING_TAG 'Sncs' + +// +// Structure that contains all the global data structures +// used throughout the scanner. +// + +SCANNER_DATA ScannerData; + +// +// This is a static list of file name extensions files we are interested in scanning +// + +PUNICODE_STRING ScannedExtensions; +ULONG ScannedExtensionCount; + +// +// The default extension to scan if not configured in the registry +// + +UNICODE_STRING ScannedExtensionDefault = RTL_CONSTANT_STRING( L"doc" ); + +// +// Function prototypes +// + +NTSTATUS +ScannerInitializeScannedExtensions( + _In_ PUNICODE_STRING RegistryPath + ); + +VOID +ScannerFreeExtensions( + ); + +NTSTATUS +ScannerAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +VOID +ScannerFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +NTSTATUS +ScannerPortConnect ( + _In_ PFLT_PORT ClientPort, + _In_opt_ PVOID ServerPortCookie, + _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Outptr_result_maybenull_ PVOID *ConnectionCookie + ); + +VOID +ScannerPortDisconnect ( + _In_opt_ PVOID ConnectionCookie + ); + +NTSTATUS +ScannerpScanFileInUserMode ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PBOOLEAN SafeToOpen + ); + +BOOLEAN +ScannerpCheckExtension ( + _In_ PUNICODE_STRING Extension + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, DriverEntry) + #pragma alloc_text(INIT, ScannerInitializeScannedExtensions) + #pragma alloc_text(PAGE, ScannerInstanceSetup) + #pragma alloc_text(PAGE, ScannerPreCreate) + #pragma alloc_text(PAGE, ScannerPortConnect) + #pragma alloc_text(PAGE, ScannerPortDisconnect) + #pragma alloc_text(PAGE, ScannerFreeExtensions) + #pragma alloc_text(PAGE, ScannerAllocateUnicodeString) + #pragma alloc_text(PAGE, ScannerFreeUnicodeString) +#endif + + +// +// Constant FLT_REGISTRATION structure for our filter. This +// initializes the callback routines our filter wants to register +// for. This is only used to register with the filter manager +// + +const FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + 0, + ScannerPreCreate, + ScannerPostCreate}, + + { IRP_MJ_CLEANUP, + 0, + ScannerPreCleanup, + NULL}, + + { IRP_MJ_WRITE, + 0, + ScannerPreWrite, + NULL}, + +#if (WINVER>=0x0602) + + { IRP_MJ_FILE_SYSTEM_CONTROL, + 0, + ScannerPreFileSystemControl, + NULL + }, + +#endif + + { IRP_MJ_OPERATION_END} +}; + + +const FLT_CONTEXT_REGISTRATION ContextRegistration[] = { + + { FLT_STREAMHANDLE_CONTEXT, + 0, + NULL, + sizeof(SCANNER_STREAM_HANDLE_CONTEXT), + 'chBS' }, + + { FLT_CONTEXT_END } +}; + +const FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + ContextRegistration, // Context Registration. + Callbacks, // Operation callbacks + ScannerUnload, // FilterUnload + ScannerInstanceSetup, // InstanceSetup + ScannerQueryTeardown, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent +}; + +//////////////////////////////////////////////////////////////////////////// +// +// Filter initialization and unload routines. +// +//////////////////////////////////////////////////////////////////////////// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine for the Filter driver. This + registers the Filter 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. +--*/ +{ + OBJECT_ATTRIBUTES oa; + UNICODE_STRING uniString; + PSECURITY_DESCRIPTOR sd; + NTSTATUS status; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Register with filter manager. + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &ScannerData.Filter ); + + + if (!NT_SUCCESS( status )) { + + return status; + } + + // + // Obtain the extensions to scan from the registry + // + + status = ScannerInitializeScannedExtensions( RegistryPath ); + + if (!NT_SUCCESS( status )) { + + status = STATUS_SUCCESS; + + ScannedExtensions = &ScannedExtensionDefault; + ScannedExtensionCount = 1; + } + + // + // Create a communication port. + // + + RtlInitUnicodeString( &uniString, ScannerPortName ); + + // + // We secure the port so only ADMINs & SYSTEM can acecss it. + // + + status = FltBuildDefaultSecurityDescriptor( &sd, FLT_PORT_ALL_ACCESS ); + + if (NT_SUCCESS( status )) { + + InitializeObjectAttributes( &oa, + &uniString, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + sd ); + + status = FltCreateCommunicationPort( ScannerData.Filter, + &ScannerData.ServerPort, + &oa, + NULL, + ScannerPortConnect, + ScannerPortDisconnect, + NULL, + 1 ); + // + // Free the security descriptor in all cases. It is not needed once + // the call to FltCreateCommunicationPort() is made. + // + + FltFreeSecurityDescriptor( sd ); + + if (NT_SUCCESS( status )) { + + // + // Start filtering I/O. + // + + status = FltStartFiltering( ScannerData.Filter ); + + if (NT_SUCCESS( status )) { + + return STATUS_SUCCESS; + } + + FltCloseCommunicationPort( ScannerData.ServerPort ); + } + } + + ScannerFreeExtensions(); + + FltUnregisterFilter( ScannerData.Filter ); + + return status; +} + + +NTSTATUS +ScannerInitializeScannedExtensions( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Descrition: + + This routine sets the the extensions for files to be scanned based + on 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; + PKEY_VALUE_PARTIAL_INFORMATION valueBuffer = NULL; + ULONG valueLength = 0; + BOOLEAN closeHandle = FALSE; + PWCHAR ch; + SIZE_T length; + ULONG count; + PUNICODE_STRING ext; + + PAGED_CODE(); + + ScannedExtensions = NULL; + ScannedExtensionCount = 0; + + // + // 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 ScannerInitializeScannedExtensionsCleanup; + } + + closeHandle = TRUE; + + // + // Query the length of the reg value + // + + RtlInitUnicodeString( &valueName, L"Extensions" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + NULL, + 0, + &valueLength ); + + if (status!=STATUS_BUFFER_TOO_SMALL && status!=STATUS_BUFFER_OVERFLOW) { + + status = STATUS_INVALID_PARAMETER; + goto ScannerInitializeScannedExtensionsCleanup; + } + + // + // Extract the path. + // + + valueBuffer = ExAllocatePoolWithTag( NonPagedPool, + valueLength, + SCANNER_REG_TAG ); + + if (valueBuffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto ScannerInitializeScannedExtensionsCleanup; + } + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + valueBuffer, + valueLength, + &valueLength ); + + if (!NT_SUCCESS( status )) { + + goto ScannerInitializeScannedExtensionsCleanup; + } + + ch = (PWCHAR)(valueBuffer->Data); + + count = 0; + + // + // Count how many strings are in the multi string + // + + while (*ch != '\0') { + + ch = ch + wcslen( ch ) + 1; + count++; + } + + ScannedExtensions = ExAllocatePoolWithTag( PagedPool, + count * sizeof(UNICODE_STRING), + SCANNER_STRING_TAG ); + + if (ScannedExtensions == NULL) { + goto ScannerInitializeScannedExtensionsCleanup; + } + + ch = (PWCHAR)((PKEY_VALUE_PARTIAL_INFORMATION)valueBuffer->Data); + ext = ScannedExtensions; + + while (ScannedExtensionCount < count) { + + length = wcslen( ch ) * sizeof(WCHAR); + + ext->MaximumLength = (USHORT) length; + + status = ScannerAllocateUnicodeString( ext ); + + if (!NT_SUCCESS( status )) { + goto ScannerInitializeScannedExtensionsCleanup; + } + + ext->Length = (USHORT)length; + + RtlCopyMemory( ext->Buffer, ch, length ); + + ch = ch + length/sizeof(WCHAR) + 1; + + ScannedExtensionCount++; + + ext++; + + } + +ScannerInitializeScannedExtensionsCleanup: + + // + // Note that this function leaks the global buffers. + // On failure DriverEntry will clean up the globals + // so we don't have to do that here. + // + + if (valueBuffer != NULL) { + + ExFreePoolWithTag( valueBuffer, SCANNER_REG_TAG ); + valueBuffer = NULL; + } + + if (closeHandle) { + + ZwClose( driverRegKey ); + } + + if (!NT_SUCCESS( status )) { + + ScannerFreeExtensions(); + } + + return status; +} + + +VOID +ScannerFreeExtensions( + ) +/*++ + +Routine Descrition: + + This routine cleans up the global buffers on both + teardown and initialization failure. + +Arguments: + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + // + // Free the strings in the scanned extension array + // + + while (ScannedExtensionCount > 0) { + + ScannedExtensionCount--; + + if (ScannedExtensions != &ScannedExtensionDefault) { + + ScannerFreeUnicodeString( ScannedExtensions + ScannedExtensionCount ); + } + } + + if (ScannedExtensions != &ScannedExtensionDefault && ScannedExtensions != NULL) { + + ExFreePoolWithTag( ScannedExtensions, SCANNER_STRING_TAG ); + } + + ScannedExtensions = NULL; + +} + + +NTSTATUS +ScannerAllocateUnicodeString ( + _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( NonPagedPool, + String->MaximumLength, + SCANNER_STRING_TAG ); + + if (String->Buffer == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + String->Length = 0; + + return STATUS_SUCCESS; +} + + +VOID +ScannerFreeUnicodeString ( + _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(); + + if (String->Buffer) { + + ExFreePoolWithTag( String->Buffer, + SCANNER_STRING_TAG ); + String->Buffer = NULL; + } + + String->Length = String->MaximumLength = 0; + String->Buffer = NULL; +} + + +NTSTATUS +ScannerPortConnect ( + _In_ PFLT_PORT ClientPort, + _In_opt_ PVOID ServerPortCookie, + _In_reads_bytes_opt_(SizeOfContext) PVOID ConnectionContext, + _In_ ULONG SizeOfContext, + _Outptr_result_maybenull_ PVOID *ConnectionCookie + ) +/*++ + +Routine Description + + This is called when user-mode connects to the server port - to establish a + connection + +Arguments + + ClientPort - This is the client connection port that will be used to + send messages from the filter + + ServerPortCookie - The context associated with this port when the + minifilter created this port. + + ConnectionContext - Context from entity connecting to this port (most likely + your user mode service) + + SizeofContext - Size of ConnectionContext in bytes + + ConnectionCookie - Context to be passed to the port disconnect routine. + +Return Value + + STATUS_SUCCESS - to accept the connection + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ServerPortCookie ); + UNREFERENCED_PARAMETER( ConnectionContext ); + UNREFERENCED_PARAMETER( SizeOfContext); + UNREFERENCED_PARAMETER( ConnectionCookie = NULL ); + + FLT_ASSERT( ScannerData.ClientPort == NULL ); + FLT_ASSERT( ScannerData.UserProcess == NULL ); + + // + // Set the user process and port. In a production filter it may + // be necessary to synchronize access to such fields with port + // lifetime. For instance, while filter manager will synchronize + // FltCloseClientPort with FltSendMessage's reading of the port + // handle, synchronizing access to the UserProcess would be up to + // the filter. + // + + ScannerData.UserProcess = PsGetCurrentProcess(); + ScannerData.ClientPort = ClientPort; + + DbgPrint( "!!! scanner.sys --- connected, port=0x%p\n", ClientPort ); + + return STATUS_SUCCESS; +} + + +VOID +ScannerPortDisconnect( + _In_opt_ PVOID ConnectionCookie + ) +/*++ + +Routine Description + + This is called when the connection is torn-down. We use it to close our + handle to the connection + +Arguments + + ConnectionCookie - Context from the port connect routine + +Return value + + None + +--*/ +{ + UNREFERENCED_PARAMETER( ConnectionCookie ); + + PAGED_CODE(); + + DbgPrint( "!!! scanner.sys --- disconnected, port=0x%p\n", ScannerData.ClientPort ); + + // + // Close our handle to the connection: note, since we limited max connections to 1, + // another connect will not be allowed until we return from the disconnect routine. + // + + FltCloseClientPort( ScannerData.Filter, &ScannerData.ClientPort ); + + // + // Reset the user-process field. + // + + ScannerData.UserProcess = NULL; +} + + +NTSTATUS +ScannerUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the unload routine for the Filter driver. This unregisters the + Filter with the filter manager and frees any allocated global data + structures. + +Arguments: + + None. + +Return Value: + + Returns the final status of the deallocation routines. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + ScannerFreeExtensions(); + + // + // Close the server port. + // + + FltCloseCommunicationPort( ScannerData.ServerPort ); + + // + // Unregister the filter + // + + FltUnregisterFilter( ScannerData.Filter ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +ScannerInstanceSetup ( + _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 by the filter manager when a new instance is created. + We specified in the registry that we only want for manual attachments, + so that is all we should receive here. + +Arguments: + + FltObjects - Describes the instance and volume which we are being asked to + setup. + + Flags - Flags describing the type of attachment this is. + + VolumeDeviceType - The DEVICE_TYPE for the volume to which this instance + will attach. + + VolumeFileSystemType - The file system formatted on this volume. + +Return Value: + + STATUS_SUCCESS - we wish to attach to the volume + STATUS_FLT_DO_NOT_ATTACH - no, thank you + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + FLT_ASSERT( FltObjects->Filter == ScannerData.Filter ); + + // + // Don't attach to network volumes. + // + + if (VolumeDeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) { + + return STATUS_FLT_DO_NOT_ATTACH; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +ScannerQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ) +/*++ + +Routine Description: + + This is the instance detach routine for the filter. This + routine is called by filter manager when a user initiates a manual instance + detach. This is a 'query' routine: if the filter does not want to support + manual detach, it can return a failure status + +Arguments: + + FltObjects - Describes the instance and volume for which we are receiving + this query teardown request. + + Flags - Unused + +Return Value: + + STATUS_SUCCESS - we allow instance detach to happen + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + return STATUS_SUCCESS; +} + + +FLT_PREOP_CALLBACK_STATUS +ScannerPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre create callback. We need to remember whether this file has been + opened for write access. If it has, we'll want to rescan it in cleanup. + This scheme results in extra scans in at least two cases: + -- if the create fails (perhaps for access denied) + -- the file is opened for write access but never actually written to + The assumption is that writes are more common than creates, and checking + or setting the context in the write path would be less efficient than + taking a good guess before the create. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - Output parameter which can be used to pass a context + from this pre-create callback to the post-create callback. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - If this is not our user-mode process. + FLT_PREOP_SUCCESS_NO_CALLBACK - All other threads. + +--*/ +{ + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext = NULL ); + + PAGED_CODE(); + + // + // See if this create is being done by our user process. + // + + if (IoThreadToProcess( Data->Thread ) == ScannerData.UserProcess) { + + DbgPrint( "!!! scanner.sys -- allowing create for trusted process \n" ); + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + return FLT_PREOP_SUCCESS_WITH_CALLBACK; +} + + +BOOLEAN +ScannerpCheckExtension ( + _In_ PUNICODE_STRING Extension + ) +/*++ + +Routine Description: + + Checks if this file name extension is something we are interested in + +Arguments + + Extension - Pointer to the file name extension + +Return Value + + TRUE - Yes we are interested + FALSE - No +--*/ +{ + ULONG count; + + if (Extension->Length == 0) { + + return FALSE; + } + + // + // Check if it matches any one of our static extension list + // + + for (count = 0; count < ScannedExtensionCount; count++) { + + if (RtlCompareUnicodeString( Extension, ScannedExtensions + count, TRUE ) == 0) { + + // + // A match. We are interested in this file + // + + return TRUE; + } + } + + return FALSE; +} + + +FLT_POSTOP_CALLBACK_STATUS +ScannerPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + Post create callback. We can't scan the file until after the create has + gone to the filesystem, since otherwise the filesystem wouldn't be ready + to read the file for us. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - The operation context passed fron the pre-create + callback. + + Flags - Flags to say why we are getting this post-operation callback. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - ok to open the file or we wish to deny + access to this file, hence undo the open + +--*/ +{ + PSCANNER_STREAM_HANDLE_CONTEXT scannerContext; + FLT_POSTOP_CALLBACK_STATUS returnStatus = FLT_POSTOP_FINISHED_PROCESSING; + PFLT_FILE_NAME_INFORMATION nameInfo; + NTSTATUS status; + BOOLEAN safeToOpen, scanFile; + + UNREFERENCED_PARAMETER( CompletionContext ); + UNREFERENCED_PARAMETER( Flags ); + + // + // If this create was failing anyway, don't bother scanning now. + // + + if (!NT_SUCCESS( Data->IoStatus.Status ) || + (STATUS_REPARSE == Data->IoStatus.Status)) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + // + // Check if we are interested in this file. + // + + status = FltGetFileNameInformation( Data, + FLT_FILE_NAME_NORMALIZED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + + if (!NT_SUCCESS( status )) { + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + FltParseFileNameInformation( nameInfo ); + + // + // Check if the extension matches the list of extensions we are interested in + // + + scanFile = ScannerpCheckExtension( &nameInfo->Extension ); + + // + // Release file name info, we're done with it + // + + FltReleaseFileNameInformation( nameInfo ); + + if (!scanFile) { + + // + // Not an extension we are interested in + // + + return FLT_POSTOP_FINISHED_PROCESSING; + } + + (VOID) ScannerpScanFileInUserMode( FltObjects->Instance, + FltObjects->FileObject, + &safeToOpen ); + + if (!safeToOpen) { + + // + // Ask the filter manager to undo the create. + // + + DbgPrint( "!!! scanner.sys -- foul language detected in postcreate !!!\n" ); + + DbgPrint( "!!! scanner.sys -- undoing create \n" ); + + FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject ); + + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + Data->IoStatus.Information = 0; + + returnStatus = FLT_POSTOP_FINISHED_PROCESSING; + + } else if (FltObjects->FileObject->WriteAccess) { + + // + // + // The create has requested write access, mark to rescan the file. + // Allocate the context. + // + + status = FltAllocateContext( ScannerData.Filter, + FLT_STREAMHANDLE_CONTEXT, + sizeof(SCANNER_STREAM_HANDLE_CONTEXT), + PagedPool, + &scannerContext ); + + if (NT_SUCCESS(status)) { + + // + // Set the handle context. + // + + scannerContext->RescanRequired = TRUE; + + (VOID) FltSetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + FLT_SET_CONTEXT_REPLACE_IF_EXISTS, + scannerContext, + NULL ); + + // + // Normally we would check the results of FltSetStreamHandleContext + // for a variety of error cases. However, The only error status + // that could be returned, in this case, would tell us that + // contexts are not supported. Even if we got this error, + // we just want to release the context now and that will free + // this memory if it was not successfully set. + // + + // + // Release our reference on the context (the set adds a reference) + // + + FltReleaseContext( scannerContext ); + } + } + + return returnStatus; +} + + +FLT_PREOP_CALLBACK_STATUS +ScannerPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre cleanup callback. If this file was opened for write access, we want + to rescan it now. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - Output parameter which can be used to pass a context + from this pre-cleanup callback to the post-cleanup callback. + +Return Value: + + Always FLT_PREOP_SUCCESS_NO_CALLBACK. + +--*/ +{ + NTSTATUS status; + PSCANNER_STREAM_HANDLE_CONTEXT context; + BOOLEAN safe; + + UNREFERENCED_PARAMETER( Data ); + UNREFERENCED_PARAMETER( CompletionContext ); + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &context ); + + if (NT_SUCCESS( status )) { + + if (context->RescanRequired) { + + (VOID) ScannerpScanFileInUserMode( FltObjects->Instance, + FltObjects->FileObject, + &safe ); + + if (!safe) { + + DbgPrint( "!!! scanner.sys -- foul language detected in precleanup !!!\n" ); + } + } + + FltReleaseContext( context ); + } + + + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} + + +FLT_PREOP_CALLBACK_STATUS +ScannerPreWrite ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre write callback. We want to scan what's being written now. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - Output parameter which can be used to pass a context + from this pre-write callback to the post-write callback. + +Return Value: + + Always FLT_PREOP_SUCCESS_NO_CALLBACK. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + NTSTATUS status; + PSCANNER_NOTIFICATION notification = NULL; + PSCANNER_STREAM_HANDLE_CONTEXT context = NULL; + ULONG replyLength; + BOOLEAN safe = TRUE; + PUCHAR buffer; + + UNREFERENCED_PARAMETER( CompletionContext ); + + // + // If not client port just ignore this write. + // + + if (ScannerData.ClientPort == NULL) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &context ); + + if (!NT_SUCCESS( status )) { + + // + // We are not interested in this file + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Use try-finally to cleanup + // + + try { + + // + // Pass the contents of the buffer to user mode. + // + + if (Data->Iopb->Parameters.Write.Length != 0) { + + // + // Get the users buffer address. If there is a MDL defined, use + // it. If not use the given buffer address. + // + + if (Data->Iopb->Parameters.Write.MdlAddress != NULL) { + + buffer = MmGetSystemAddressForMdlSafe( Data->Iopb->Parameters.Write.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + // + // If we have a MDL but could not get and address, we ran out + // of memory, report the correct error + // + + if (buffer == NULL) { + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + returnStatus = FLT_PREOP_COMPLETE; + leave; + } + + } else { + + // + // Use the users buffer + // + + buffer = Data->Iopb->Parameters.Write.WriteBuffer; + } + + // + // In a production-level filter, we would actually let user mode scan the file directly. + // Allocating & freeing huge amounts of non-paged pool like this is not very good for system perf. + // This is just a sample! + // + + notification = ExAllocatePoolWithTag( NonPagedPool, + sizeof( SCANNER_NOTIFICATION ), + 'nacS' ); + if (notification == NULL) { + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + returnStatus = FLT_PREOP_COMPLETE; + leave; + } + + notification->BytesToScan = min( Data->Iopb->Parameters.Write.Length, SCANNER_READ_BUFFER_SIZE ); + + // + // The buffer can be a raw user buffer. Protect access to it + // + + try { + + RtlCopyMemory( ¬ification->Contents, + buffer, + notification->BytesToScan ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Error accessing buffer. Complete i/o with failure + // + + Data->IoStatus.Status = GetExceptionCode() ; + Data->IoStatus.Information = 0; + returnStatus = FLT_PREOP_COMPLETE; + leave; + } + + // + // Send message to user mode to indicate it should scan the buffer. + // We don't have to synchronize between the send and close of the handle + // as FltSendMessage takes care of that. + // + + replyLength = sizeof( SCANNER_REPLY ); + + status = FltSendMessage( ScannerData.Filter, + &ScannerData.ClientPort, + notification, + sizeof( SCANNER_NOTIFICATION ), + notification, + &replyLength, + NULL ); + + if (STATUS_SUCCESS == status) { + + safe = ((PSCANNER_REPLY) notification)->SafeToOpen; + + } else { + + // + // Couldn't send message. This sample will let the i/o through. + // + + DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status ); + } + } + + if (!safe) { + + // + // Block this write if not paging i/o (as a result of course, this scanner will not prevent memory mapped writes of contaminated + // strings to the file, but only regular writes). The effect of getting ERROR_ACCESS_DENIED for many apps to delete the file they + // are trying to write usually. + // To handle memory mapped writes - we should be scanning at close time (which is when we can really establish that the file object + // is not going to be used for any more writes) + // + + DbgPrint( "!!! scanner.sys -- foul language detected in write !!!\n" ); + + if (!FlagOn( Data->Iopb->IrpFlags, IRP_PAGING_IO )) { + + DbgPrint( "!!! scanner.sys -- blocking the write !!!\n" ); + + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + Data->IoStatus.Information = 0; + returnStatus = FLT_PREOP_COMPLETE; + } + } + + } finally { + + if (notification != NULL) { + + ExFreePoolWithTag( notification, 'nacS' ); + } + + if (context) { + + FltReleaseContext( context ); + } + } + + return returnStatus; +} + +#if (WINVER>=0x0602) + +FLT_PREOP_CALLBACK_STATUS +ScannerPreFileSystemControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre FS Control callback. + +Arguments: + + Data - The structure which describes the operation parameters. + + FltObject - The structure which describes the objects affected by this + operation. + + CompletionContext - Output parameter which can be used to pass a context + from this callback to the post-write callback. + +Return Value: + + FLT_PREOP_SUCCESS_NO_CALLBACK or FLT_PREOP_COMPLETE + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + NTSTATUS status; + ULONG fsControlCode; + PSCANNER_STREAM_HANDLE_CONTEXT context = NULL; + + UNREFERENCED_PARAMETER( CompletionContext ); + + FLT_ASSERT( Data != NULL ); + FLT_ASSERT( Data->Iopb != NULL ); + + // + // If not client port just ignore this write. + // + + if (ScannerData.ClientPort == NULL) { + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + status = FltGetStreamHandleContext( FltObjects->Instance, + FltObjects->FileObject, + &context ); + + if (!NT_SUCCESS( status )) { + + // + // We are not interested in this file + // + + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } + + // + // Use try-finally to cleanup + // + + try { + + fsControlCode = Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode; + + if (fsControlCode == FSCTL_OFFLOAD_WRITE) { + + // + // Scanner cannot access the data in this offload write request. + // In a production-level filter, we would actually let user mode + // scan the file after offload write completes (on cleanup etc). + // Since this is just a sample, block offload write with + // STATUS_ACCESS_DENIED, although this is not an acceptable + // production-level behavior. + // + + DbgPrint( "!!! scanner.sys -- blocking the offload write !!!\n" ); + + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + Data->IoStatus.Information = 0; + + returnStatus = FLT_PREOP_COMPLETE; + } + + } finally { + + if (context) { + + FltReleaseContext( context ); + } + } + + return returnStatus; +} + +#endif + +////////////////////////////////////////////////////////////////////////// +// Local support routines. +// +///////////////////////////////////////////////////////////////////////// + +NTSTATUS +ScannerpScanFileInUserMode ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_ PBOOLEAN SafeToOpen + ) +/*++ + +Routine Description: + + This routine is called to send a request up to user mode to scan a given + file and tell our caller whether it's safe to open this file. + + Note that if the scan fails, we set SafeToOpen to TRUE. The scan may fail + because the service hasn't started, or perhaps because this create/cleanup + is for a directory, and there's no data to read & scan. + + If we failed creates when the service isn't running, there'd be a + bootstrapping problem -- how would we ever load the .exe for the service? + +Arguments: + + Instance - Handle to the filter instance for the scanner on this volume. + + FileObject - File to be scanned. + + SafeToOpen - Set to FALSE if the file is scanned successfully and it contains + foul language. + +Return Value: + + The status of the operation, hopefully STATUS_SUCCESS. The common failure + status will probably be STATUS_INSUFFICIENT_RESOURCES. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PVOID buffer = NULL; + ULONG bytesRead; + PSCANNER_NOTIFICATION notification = NULL; + FLT_VOLUME_PROPERTIES volumeProps; + LARGE_INTEGER offset; + ULONG replyLength, length; + PFLT_VOLUME volume = NULL; + + *SafeToOpen = TRUE; + + // + // If not client port just return. + // + + if (ScannerData.ClientPort == NULL) { + + return STATUS_SUCCESS; + } + + try { + + // + // Obtain the volume object . + // + + status = FltGetVolumeFromInstance( Instance, &volume ); + + if (!NT_SUCCESS( status )) { + + leave; + } + + // + // Determine sector size. Noncached I/O can only be done at sector size offsets, and in lengths which are + // multiples of sector size. A more efficient way is to make this call once and remember the sector size in the + // instance setup routine and setup an instance context where we can cache it. + // + + status = FltGetVolumeProperties( volume, + &volumeProps, + sizeof( volumeProps ), + &length ); + // + // STATUS_BUFFER_OVERFLOW can be returned - however we only need the properties, not the names + // hence we only check for error status. + // + + if (NT_ERROR( status )) { + + leave; + } + + length = max( SCANNER_READ_BUFFER_SIZE, volumeProps.SectorSize ); + + // + // Use non-buffered i/o, so allocate aligned pool + // + + buffer = FltAllocatePoolAlignedWithTag( Instance, + NonPagedPool, + length, + 'nacS' ); + + if (NULL == buffer) { + + status = STATUS_INSUFFICIENT_RESOURCES; + leave; + } + + notification = ExAllocatePoolWithTag( NonPagedPool, + sizeof( SCANNER_NOTIFICATION ), + 'nacS' ); + + if(NULL == notification) { + + status = STATUS_INSUFFICIENT_RESOURCES; + leave; + } + + // + // Read the beginning of the file and pass the contents to user mode. + // + + offset.QuadPart = bytesRead = 0; + status = FltReadFile( Instance, + FileObject, + &offset, + length, + buffer, + FLTFL_IO_OPERATION_NON_CACHED | + FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET, + &bytesRead, + NULL, + NULL ); + + if (NT_SUCCESS( status ) && (0 != bytesRead)) { + + notification->BytesToScan = (ULONG) bytesRead; + + // + // Copy only as much as the buffer can hold + // + + RtlCopyMemory( ¬ification->Contents, + buffer, + min( notification->BytesToScan, SCANNER_READ_BUFFER_SIZE ) ); + + replyLength = sizeof( SCANNER_REPLY ); + + status = FltSendMessage( ScannerData.Filter, + &ScannerData.ClientPort, + notification, + sizeof(SCANNER_NOTIFICATION), + notification, + &replyLength, + NULL ); + + if (STATUS_SUCCESS == status) { + + *SafeToOpen = ((PSCANNER_REPLY) notification)->SafeToOpen; + + } else { + + // + // Couldn't send message + // + + DbgPrint( "!!! scanner.sys --- couldn't send message to user-mode to scan file, status 0x%X\n", status ); + } + } + + } finally { + + if (NULL != buffer) { + + FltFreePoolAlignedWithTag( Instance, buffer, 'nacS' ); + } + + if (NULL != notification) { + + ExFreePoolWithTag( notification, 'nacS' ); + } + + if (NULL != volume) { + + FltObjectDereference( volume ); + } + } + + return status; +} + diff --git a/filesys/miniFilter/scanner/filter/scanner.h b/filesys/miniFilter/scanner/filter/scanner.h new file mode 100644 index 00000000..c45592e9 --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.h @@ -0,0 +1,160 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scrubber.h + +Abstract: + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + only visible within the kernel. + +Environment: + + Kernel mode + +--*/ +#ifndef __SCANNER_H__ +#define __SCANNER_H__ + + +/////////////////////////////////////////////////////////////////////////// +// +// Global variables +// +/////////////////////////////////////////////////////////////////////////// + + +typedef struct _SCANNER_DATA { + + // + // The object that identifies this driver. + // + + PDRIVER_OBJECT DriverObject; + + // + // The filter handle that results from a call to + // FltRegisterFilter. + // + + PFLT_FILTER Filter; + + // + // Listens for incoming connections + // + + PFLT_PORT ServerPort; + + // + // User process that connected to the port + // + + PEPROCESS UserProcess; + + // + // Client port for a connection to user-mode + // + + PFLT_PORT ClientPort; + +} SCANNER_DATA, *PSCANNER_DATA; + +extern SCANNER_DATA ScannerData; + +typedef struct _SCANNER_STREAM_HANDLE_CONTEXT { + + BOOLEAN RescanRequired; + +} SCANNER_STREAM_HANDLE_CONTEXT, *PSCANNER_STREAM_HANDLE_CONTEXT; + +#pragma warning(push) +#pragma warning(disable:4200) // disable warnings for structures with zero length arrays. + +typedef struct _SCANNER_CREATE_PARAMS { + + WCHAR String[0]; + +} SCANNER_CREATE_PARAMS, *PSCANNER_CREATE_PARAMS; + +#pragma warning(pop) + + +/////////////////////////////////////////////////////////////////////////// +// +// Prototypes for the startup and unload routines used for +// this Filter. +// +// Implementation in scanner.c +// +/////////////////////////////////////////////////////////////////////////// +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +ScannerUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +ScannerQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +ScannerPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +ScannerPostCreate ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_opt_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +ScannerPreCleanup ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +ScannerPreWrite ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +#if (WINVER >= 0x0602) + +FLT_PREOP_CALLBACK_STATUS +ScannerPreFileSystemControl ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +#endif + +NTSTATUS +ScannerInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +#endif /* __SCANNER_H__ */ + diff --git a/filesys/miniFilter/scanner/filter/scanner.rc b/filesys/miniFilter/scanner/filter/scanner.rc new file mode 100644 index 00000000..9269b6cf --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Scanner Filter" +#define VER_INTERNALNAME_STR "scanner.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/scanner/filter/scanner.vcxproj b/filesys/miniFilter/scanner/filter/scanner.vcxproj new file mode 100644 index 00000000..99432692 --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.vcxproj @@ -0,0 +1,192 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{82607450-8E93-44E4-9EDC-AA0C310E0E64}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>scanner</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>scanner</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>scanner</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>scanner</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="scanner.c" /> + <ResourceCompile Include="scanner.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters b/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters new file mode 100644 index 00000000..37511409 --- /dev/null +++ b/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{714BE986-D639-4C08-B0B5-CEDD7F5A24B3}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{8E4DEA17-46F6-459E-91CA-8EB73909B60A}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{A4702A45-F819-43D0-B05B-56FFCA504075}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{A873A433-3F6A-4828-9BDA-9FDF6C9D4726}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="scanner.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="scanner.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/scanner/inc/scanuk.h b/filesys/miniFilter/scanner/inc/scanuk.h new file mode 100644 index 00000000..433657f6 --- /dev/null +++ b/filesys/miniFilter/scanner/inc/scanuk.h @@ -0,0 +1,49 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scanuk.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes that are + shared between kernel and user mode. + +Environment: + + Kernel & user mode + +--*/ + +#ifndef __SCANUK_H__ +#define __SCANUK_H__ + +// +// Name of port used to communicate +// + +const PWSTR ScannerPortName = L"\\ScannerPort"; + + +#define SCANNER_READ_BUFFER_SIZE 1024 + +typedef struct _SCANNER_NOTIFICATION { + + ULONG BytesToScan; + ULONG Reserved; // for quad-word alignement of the Contents structure + UCHAR Contents[SCANNER_READ_BUFFER_SIZE]; + +} SCANNER_NOTIFICATION, *PSCANNER_NOTIFICATION; + +typedef struct _SCANNER_REPLY { + + BOOLEAN SafeToOpen; + +} SCANNER_REPLY, *PSCANNER_REPLY; + +#endif // __SCANUK_H__ + + diff --git a/filesys/miniFilter/scanner/scanner.inf b/filesys/miniFilter/scanner/scanner.inf new file mode 100644 index 00000000..e4a06fc5 --- /dev/null +++ b/filesys/miniFilter/scanner/scanner.inf @@ -0,0 +1,104 @@ +;;; +;;; Scanner +;;; +;;; +;;; Copyright (c) 1999-2002, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ContentScreener" ;This is determined by the work this filter driver does +ClassGuid = {3e3f0674-c83c-4558-bb26-9820e1eba5c5} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.0 +CatalogFile = scanner.cat + + +[DestinationDirs] +DefaultDestDir = 12 +Scanner.DriverFiles = 12 ;%windir%\system32\drivers +Scanner.UserFiles = 10,FltMgr ;%windir%\FltMgr + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = Scanner.DriverFiles, Scanner.UserFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,Scanner.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = Scanner.DriverFiles, Scanner.UserFiles + + + +[DefaultUninstall.Services] +DelService = Scanner,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[Scanner.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Content Screener" +AddReg = Scanner.AddRegistry + +; +; Registry Modifications +; + +[Scanner.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% +HKR,,"Extensions",0x00010000,"exe","doc","txt","bat","cmd","inf" + +; +; Copy Files +; + +[Scanner.DriverFiles] +%DriverName%.sys + +[Scanner.UserFiles] +%UserAppName%.exe + +[SourceDisksFiles] +scanner.sys = 1,, +scanuser.exe = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Scanner mini-filter driver" +ServiceName = "Scanner" +DriverName = "scanner" +UserAppName = "scanuser" +DiskId1 = "Scanner Device Installation Disk" + +;Instances specific information. +DefaultInstance = "Scanner Instance" +Instance1.Name = "Scanner Instance" +Instance1.Altitude = "265000" +Instance1.Flags = 0x0 ; Allow all attachments diff --git a/filesys/miniFilter/scanner/scanner.sln b/filesys/miniFilter/scanner/scanner.sln new file mode 100644 index 00000000..f79705b4 --- /dev/null +++ b/filesys/miniFilter/scanner/scanner.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{826E5DA7-B03B-43C0-9CFF-E0B6E29942A3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{B27B6441-52BC-4DFD-9A25-1BC5972664F4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanner", "filter\scanner.vcxproj", "{6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanuser", "user\scanuser.vcxproj", "{EF224A50-E448-4767-AD5E-1EEF058D0E50}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|Win32.ActiveCfg = Debug|Win32 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|Win32.Build.0 = Debug|Win32 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|Win32.ActiveCfg = Release|Win32 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|Win32.Build.0 = Release|Win32 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|x64.ActiveCfg = Debug|x64 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Debug|x64.Build.0 = Debug|x64 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|x64.ActiveCfg = Release|x64 + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3}.Release|x64.Build.0 = Release|x64 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|Win32.ActiveCfg = Debug|Win32 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|Win32.Build.0 = Debug|Win32 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|Win32.ActiveCfg = Release|Win32 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|Win32.Build.0 = Release|Win32 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|x64.ActiveCfg = Debug|x64 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Debug|x64.Build.0 = Debug|x64 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|x64.ActiveCfg = Release|x64 + {EF224A50-E448-4767-AD5E-1EEF058D0E50}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6F2E7887-B782-4624-A03A-7AC8CB1D6AB3} = {826E5DA7-B03B-43C0-9CFF-E0B6E29942A3} + {EF224A50-E448-4767-AD5E-1EEF058D0E50} = {B27B6441-52BC-4DFD-9A25-1BC5972664F4} + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/scanner/user/scanUser.c b/filesys/miniFilter/scanner/user/scanUser.c new file mode 100644 index 00000000..da57b75c --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanUser.c @@ -0,0 +1,416 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scanUser.c + +Abstract: + + This file contains the implementation for the main function of the + user application piece of scanner. This function is responsible for + actually scanning file contents. + +Environment: + + User mode + +--*/ + +#include <windows.h> +#include <stdlib.h> +#include <stdio.h> +#include <winioctl.h> +#include <string.h> +#include <crtdbg.h> +#include <assert.h> +#include <fltuser.h> +#include "scanuk.h" +#include "scanuser.h" +#include <dontuse.h> + +// +// Default and Maximum number of threads. +// + +#define SCANNER_DEFAULT_REQUEST_COUNT 5 +#define SCANNER_DEFAULT_THREAD_COUNT 2 +#define SCANNER_MAX_THREAD_COUNT 64 + +UCHAR FoulString[] = "foul"; + +// +// Context passed to worker threads +// + +typedef struct _SCANNER_THREAD_CONTEXT { + + HANDLE Port; + HANDLE Completion; + +} SCANNER_THREAD_CONTEXT, *PSCANNER_THREAD_CONTEXT; + + +VOID +Usage ( + VOID + ) +/*++ + +Routine Description + + Prints usage + +Arguments + + None + +Return Value + + None + +--*/ +{ + + printf( "Connects to the scanner filter and scans buffers \n" ); + printf( "Usage: scanuser [requests per thread] [number of threads(1-64)]\n" ); +} + +BOOL +ScanBuffer ( + _In_reads_bytes_(BufferSize) PUCHAR Buffer, + _In_ ULONG BufferSize + ) +/*++ + +Routine Description + + Scans the supplied buffer for an instance of FoulString. + + Note: Pattern matching algorithm used here is just for illustration purposes, + there are many better algorithms available for real world filters + +Arguments + + Buffer - Pointer to buffer + BufferSize - Size of passed in buffer + +Return Value + + TRUE - Found an occurrence of the appropriate FoulString + FALSE - Buffer is ok + +--*/ +{ + PUCHAR p; + ULONG searchStringLength = sizeof(FoulString) - sizeof(UCHAR); + + for (p = Buffer; + p <= (Buffer + BufferSize - searchStringLength); + p++) { + + if (RtlEqualMemory( p, FoulString, searchStringLength )) { + + printf( "Found a string\n" ); + + // + // Once we find our search string, we're not interested in seeing + // whether it appears again. + // + + return TRUE; + } + } + + return FALSE; +} + + +DWORD +ScannerWorker( + _In_ PSCANNER_THREAD_CONTEXT Context + ) +/*++ + +Routine Description + + This is a worker thread that + + +Arguments + + Context - This thread context has a pointer to the port handle we use to send/receive messages, + and a completion port handle that was already associated with the comm. port by the caller + +Return Value + + HRESULT indicating the status of thread exit. + +--*/ +{ + PSCANNER_NOTIFICATION notification; + SCANNER_REPLY_MESSAGE replyMessage; + PSCANNER_MESSAGE message; + LPOVERLAPPED pOvlp; + BOOL result; + DWORD outSize; + HRESULT hr; + ULONG_PTR key; + +#pragma warning(push) +#pragma warning(disable:4127) // conditional expression is constant + + while (TRUE) { + +#pragma warning(pop) + + // + // Poll for messages from the filter component to scan. + // + + result = GetQueuedCompletionStatus( Context->Completion, &outSize, &key, &pOvlp, INFINITE ); + + // + // Obtain the message: note that the message we sent down via FltGetMessage() may NOT be + // the one dequeued off the completion queue: this is solely because there are multiple + // threads per single port handle. Any of the FilterGetMessage() issued messages can be + // completed in random order - and we will just dequeue a random one. + // + + message = CONTAINING_RECORD( pOvlp, SCANNER_MESSAGE, Ovlp ); + + if (!result) { + + // + // An error occured. + // + + hr = HRESULT_FROM_WIN32( GetLastError() ); + break; + } + + printf( "Received message, size %d\n", pOvlp->InternalHigh ); + + notification = &message->Notification; + + assert(notification->BytesToScan <= SCANNER_READ_BUFFER_SIZE); + _Analysis_assume_(notification->BytesToScan <= SCANNER_READ_BUFFER_SIZE); + + result = ScanBuffer( notification->Contents, notification->BytesToScan ); + + replyMessage.ReplyHeader.Status = 0; + replyMessage.ReplyHeader.MessageId = message->MessageHeader.MessageId; + + // + // Need to invert the boolean -- result is true if found + // foul language, in which case SafeToOpen should be set to false. + // + + replyMessage.Reply.SafeToOpen = !result; + + printf( "Replying message, SafeToOpen: %d\n", replyMessage.Reply.SafeToOpen ); + + hr = FilterReplyMessage( Context->Port, + (PFILTER_REPLY_HEADER) &replyMessage, + sizeof( replyMessage ) ); + + if (SUCCEEDED( hr )) { + + printf( "Replied message\n" ); + + } else { + + printf( "Scanner: Error replying message. Error = 0x%X\n", hr ); + break; + } + + memset( &message->Ovlp, 0, sizeof( OVERLAPPED ) ); + + hr = FilterGetMessage( Context->Port, + &message->MessageHeader, + FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ), + &message->Ovlp ); + + if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) { + + break; + } + } + + if (!SUCCEEDED( hr )) { + + if (hr == HRESULT_FROM_WIN32( ERROR_INVALID_HANDLE )) { + + // + // Scanner port disconncted. + // + + printf( "Scanner: Port is disconnected, probably due to scanner filter unloading.\n" ); + + } else { + + printf( "Scanner: Unknown error occured. Error = 0x%X\n", hr ); + } + } + + free( message ); + + return hr; +} + + +int _cdecl +main ( + _In_ int argc, + _In_reads_(argc) char *argv[] + ) +{ + DWORD requestCount = SCANNER_DEFAULT_REQUEST_COUNT; + DWORD threadCount = SCANNER_DEFAULT_THREAD_COUNT; + HANDLE threads[SCANNER_MAX_THREAD_COUNT]; + SCANNER_THREAD_CONTEXT context; + HANDLE port, completion; + PSCANNER_MESSAGE msg; + DWORD threadId; + HRESULT hr; + DWORD i, j; + + // + // Check how many threads and per thread requests are desired. + // + + if (argc > 1) { + + requestCount = atoi( argv[1] ); + + if (requestCount <= 0) { + + Usage(); + return 1; + } + + if (argc > 2) { + + threadCount = atoi( argv[2] ); + } + + if (threadCount <= 0 || threadCount > 64) { + + Usage(); + return 1; + } + } + + // + // Open a commuication channel to the filter + // + + printf( "Scanner: Connecting to the filter ...\n" ); + + hr = FilterConnectCommunicationPort( ScannerPortName, + 0, + NULL, + 0, + NULL, + &port ); + + if (IS_ERROR( hr )) { + + printf( "ERROR: Connecting to filter port: 0x%08x\n", hr ); + return 2; + } + + // + // Create a completion port to associate with this handle. + // + + completion = CreateIoCompletionPort( port, + NULL, + 0, + threadCount ); + + if (completion == NULL) { + + printf( "ERROR: Creating completion port: %d\n", GetLastError() ); + CloseHandle( port ); + return 3; + } + + printf( "Scanner: Port = 0x%p Completion = 0x%p\n", port, completion ); + + context.Port = port; + context.Completion = completion; + + // + // Create specified number of threads. + // + + for (i = 0; i < threadCount; i++) { + + threads[i] = CreateThread( NULL, + 0, + (LPTHREAD_START_ROUTINE) ScannerWorker, + &context, + 0, + &threadId ); + + if (threads[i] == NULL) { + + // + // Couldn't create thread. + // + + hr = GetLastError(); + printf( "ERROR: Couldn't create thread: %d\n", hr ); + goto main_cleanup; + } + + for (j = 0; j < requestCount; j++) { + + // + // Allocate the message. + // + +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "msg will not be leaked because it is freed in ScannerWorker") + msg = malloc( sizeof( SCANNER_MESSAGE ) ); + + if (msg == NULL) { + + hr = ERROR_NOT_ENOUGH_MEMORY; + goto main_cleanup; + } + + memset( &msg->Ovlp, 0, sizeof( OVERLAPPED ) ); + + // + // Request messages from the filter driver. + // + + hr = FilterGetMessage( port, + &msg->MessageHeader, + FIELD_OFFSET( SCANNER_MESSAGE, Ovlp ), + &msg->Ovlp ); + + if (hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING )) { + + free( msg ); + goto main_cleanup; + } + } + } + + hr = S_OK; + + WaitForMultipleObjectsEx( i, threads, TRUE, INFINITE, FALSE ); + +main_cleanup: + + printf( "Scanner: All done. Result = 0x%08x\n", hr ); + + CloseHandle( port ); + CloseHandle( completion ); + + return hr; +} + diff --git a/filesys/miniFilter/scanner/user/scanUser.rc b/filesys/miniFilter/scanner/user/scanUser.rc new file mode 100644 index 00000000..3178c5b5 --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanUser.rc @@ -0,0 +1,10 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Scanner control program" +#define VER_INTERNALNAME_STR "scanuser.exe" +#define VER_ORIGINALFILENAME_STR "scanuser.exe" + +#include "common.ver" diff --git a/filesys/miniFilter/scanner/user/scanuser.h b/filesys/miniFilter/scanner/user/scanuser.h new file mode 100644 index 00000000..6782db84 --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanuser.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) 1999-2002 Microsoft Corporation + +Module Name: + + scanuser.h + +Abstract: + + Header file which contains the structures, type definitions, + constants, global variables and function prototypes for the + user mode part of the scanner. + +Environment: + + Kernel & user mode + +--*/ +#ifndef __SCANUSER_H__ +#define __SCANUSER_H__ + +#pragma pack(1) + +typedef struct _SCANNER_MESSAGE { + + // + // Required structure header. + // + + FILTER_MESSAGE_HEADER MessageHeader; + + + // + // Private scanner-specific fields begin here. + // + + SCANNER_NOTIFICATION Notification; + + // + // Overlapped structure: this is not really part of the message + // However we embed it instead of using a separately allocated overlap structure + // + + OVERLAPPED Ovlp; + +} SCANNER_MESSAGE, *PSCANNER_MESSAGE; + +typedef struct _SCANNER_REPLY_MESSAGE { + + // + // Required structure header. + // + + FILTER_REPLY_HEADER ReplyHeader; + + // + // Private scanner-specific fields begin here. + // + + SCANNER_REPLY Reply; + +} SCANNER_REPLY_MESSAGE, *PSCANNER_REPLY_MESSAGE; + +#endif // __SCANUSER_H__ + + diff --git a/filesys/miniFilter/scanner/user/scanuser.vcxproj b/filesys/miniFilter/scanner/user/scanuser.vcxproj new file mode 100644 index 00000000..a60beb42 --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanuser.vcxproj @@ -0,0 +1,192 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{EF224A50-E448-4767-AD5E-1EEF058D0E50}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{2640B41E-C043-4A8E-B6EE-136BFFA9D855}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>Application</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>scanuser</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>scanuser</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>scanuser</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>scanuser</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(IFSKIT_INC_PATH);$(DDK_INC_PATH);..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);fltLib.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="scanUser.c" /> + <ResourceCompile Include="scanUser.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters b/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters new file mode 100644 index 00000000..468b6701 --- /dev/null +++ b/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{2CE1DA5C-939F-4E19-AECC-C2BF08201270}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{5E1F3D4B-5B51-4AD3-937E-D0FD3FC41DEC}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{FCEF13F3-09F2-4ED0-9EA6-455F60759ED5}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="scanUser.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="scanUser.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/simrep/ReadMe.md b/filesys/miniFilter/simrep/ReadMe.md new file mode 100644 index 00000000..03c6847c --- /dev/null +++ b/filesys/miniFilter/simrep/ReadMe.md @@ -0,0 +1,19 @@ +SimRep File System Minifilter Driver +==================================== + +SimRep is a sample filter that demonstrates how a file system filter can simulate file-system like reparse-point behavior to redirect a file open to an alternate path. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +Normally, if the file-system sees an open for a file with a reparse-point on it, the filesystem fills out the tag buffer and returns STATUS\_REPARSE. Minifilters see the post-operation callback for this create. As the create travels up file system filter stack in post-create path, each minifilter has the opportunity to interpret the reparse point if they own the tag. If no file system filter claims the tag, IO Manager will attempt to interpret the tag based on tags known to and serviced by IO Manager. If the tag is unknown to IO manager then the create is failed with STATUS\_IO\_REPARSE\_TAG\_NOT\_HANDLED. SimRep does not demonstrate how to handle the case where the file system hits a reparse-point on the file. Instead it "fakes" encountering a reparse point before the create reaches the filesystem. When SimRep detects a create for a path that it is redirecting, SimRep replaces the file name in the file object and completes the open with STATUS\_REPARSE. This means we reparse without actually going to the file system. + +SimRep decides to reparse according to a mapping. The mapping is made up of a "New Mapping Path" and an "Old Mapping Path". The old mapping path is the path which SimRep looks for on incoming opens. If the path specified for the create is down the Old Mapping Path, then SimRep will strip off the Old Mapping Path, and replace it with the New Mapping Path. By default, the Old Mapping Path is \\x\\y and the New Mapping Path is \\a\\b. So an open to \\x\\y\\z will be replaced with an open to \\a\\b\\z. These defaults are defined as registry keys at install time and are loaded on DriverEntry. See simrep.inf for details. + +It is important to note that SimRep does not take long and short names into account. It literally does a string comparison to detect overlap with the mapping paths. SimRep also handles IRP\_MJ\_NETWORK\_QUERY\_OPEN. Because network query opens are FastIo operations, they cannot be reparsed. This means network query opens which need to be redirected must be failed with FLT\_PREOP\_DISALLOW\_FASTIO. This will cause the Io Manager to reissue the open as a regular IRP based open. To prevent performance regression, SimRep only fails network query opens which need to be reparsed. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/simrep/simrep.c b/filesys/miniFilter/simrep/simrep.c new file mode 100644 index 00000000..4daf17ca --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.c @@ -0,0 +1,3006 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + SimRep.c + +Abstract: + +The Simulate Reparse Sample demonstrates how to return STATUS_REPARSE +on precreates. This allows the filter to redirect opens down one path +to another path. The Precreate path is complicated by network query opens +which come down as Fast IO. Fast IO cannot be redirected with Status Reparse +because reparse only works on IRP based IO. + +Simulating reparse points requires that the filter replace the name in the +file object. This will cause Driver Verifier to complain that the filter is +leaking pool and will prevent it from being unloaded. To solve this issue +SimRep attempts to use a Windows 7 Function called IoReplaceFileObjectName +which will allow IO Mgr to replace the name for us with the correct pool tag. +However, on downlevel OS Versions SimRep will go ahead and replace the name +itself. + +It is important to note that SimRep only demonstrates how to return +STATUS_REPARSE, not how to deal with file names on NT. SimRep uses two strings +to act as a mapping. When the file open name starts with the "old name mapping" +string the filter replaces it with the "new name mapping" string. This does not +take short names into account. + +SimRep can also be configured to redirect renames and creation of hardlinks. +This functionality is demonstrated in the code and can be turned on with a +registry key value indicated in the inf file. To correctly handle rename and +set link operations: +1. SimRep has to reparse opens with the SL_OPEN_TARGET_DIRECTORY flag set in + the pre-create, since this is the create that IoManager uses to open the + target of the rename. +2. SimRep implements a "pass-through" name provider. It needs to do this so + that the creates issued to resolve normalized name queries will be seen by + SimRep and it can redirect them correctly, so as to provide consistent names + to other filters. +3. SimRep has to monitor IRP_MJ_SET_INFORMATION for rename and set link + operations and re-issue them for the correct destination so that filters + below SimRep are made aware of this redirection. + +Note that SimRep simply redirects creates (and optionally renames and set +hardlink) operations. It makes no attempt to virtualize the namespace for +filters above SimRep. So the layers above SimRep will be aware of the +redirection if they query the name of the file once the create, rename or set +hardlink operation is complete. + + + +Environment: + + Kernel mode + + +--*/ + +// +// Enabled warnings +// + +#pragma warning(error:4100) // Enable-Unreferenced formal parameter +#pragma warning(error:4101) // Enable-Unreferenced local variable +#pragma warning(error:4061) // Enable-missing enumeration in switch statement +#pragma warning(error:4505) // Enable-identify dead functions + +// +// Includes +// + + +// +// This sample contains OS version specific code. If compiled for VISTA it +// will not run properly on older versions of Windows. +// +#define SIMREP_VISTA (NTDDI_VERSION >= NTDDI_VISTA) + +#include <fltKernel.h> + + +// +// Memory Pool Tags +// + +#define SIMREP_STRING_TAG 'tSpR' +#define SIMREP_REG_TAG 'eRpR' + +// +// Constants +// + +#define REPLACE_ROUTINE_NAME_STRING L"IoReplaceFileObjectName" + +#define REPLACE_QUERY_DIRECTORY_FILE_ROUTINE_NAME_STRING "FltQueryDirectoryFile" + + +// +// Context sample filter global data structures. +// + +typedef struct _MAPPING_ENTRY { + + // + // Path underwhich we want to reparse. + // + + UNICODE_STRING OldName; + + // + // Path to reparse to. + // + + UNICODE_STRING NewName; + +} MAPPING_ENTRY, *PMAPPING_ENTRY; + + +// +// Starting with windows 7, the IO Manager provides IoReplaceFileObjectName, +// but old versions of Windows will not have this function. Rather than just +// writing our own function, and forfeiting future windows functionality, we can +// use MmGetRoutineAddr, which will allow us to dynamically import IoReplaceFileObjectName +// if it exists. If not it allows us to implement the function ourselves. +// + +typedef +NTSTATUS +(* PReplaceFileObjectName ) ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ); + +typedef +NTSTATUS +(FLTAPI *PFltQueryDirectoryFile)( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(Length) PVOID FileInformationBuffer, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ); + + +typedef struct _SIMREP_GLOBAL_DATA { + + // + // Handle to minifilter returned from FltRegisterFilter() + // + + PFLT_FILTER Filter; + + // + // Structure to hold mapping information. + // + + MAPPING_ENTRY Mapping; + + // + // Pointer to the function we will use to + // replace file names. + // + + PReplaceFileObjectName ReplaceFileNameFunction; + + // + // Pointer to the function we will use to + // query directory file. + // + + PFltQueryDirectoryFile QueryDirectoryFileFunction; + + // + // Flag to control if the filter remaps renames + // + + BOOLEAN RemapRenamesAndLinks; + +#if DBG + + // + // Field to control nature of debug output + // + + ULONG DebugLevel; +#endif + +} SIMREP_GLOBAL_DATA, *PSIMREP_GLOBAL_DATA; + + +// +// 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 / detach of instances + +#define DEBUG_TRACE_REPARSE_OPERATIONS 0x00000008 // Operations that are performed to determine if we should return STATUS_REPARSE +#define DEBUG_TRACE_REPARSED_OPERATIONS 0x00000010 // Operations that return STATUS_REPARSE +#define DEBUG_TRACE_REPARSED_REISSUE 0X00000020 // Operations that need to be reissued with an IRP. + +#define DEBUG_TRACE_NAME_OPERATIONS 0x00000040 // Operations involving name provider callbacks + +#define DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS 0x00000080 // Operations involving rename or hardlink redirection + +#define DEBUG_TRACE_ALL_IO 0x00000100 // All IO operations tracked by this filter + +#define DEBUG_TRACE_ALL 0xFFFFFFFF // All flags + + +#define DebugTrace(Level, Data) \ + if ((Level) & Globals.DebugLevel) { \ + DbgPrint Data; \ + } + + +#else + +#define DebugTrace(Level, Data) {NOTHING;} + +#endif + + +// +// Function that handle driver load/unload and instance setup/cleanup +// + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +SimRepSetConfiguration( + _In_ PUNICODE_STRING RegistryPath + ); + +VOID SimRepFreeGlobals( + ); + +NTSTATUS +SimRepUnload ( + FLT_FILTER_UNLOAD_FLAGS Flags + ); + +NTSTATUS +SimRepInstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +NTSTATUS +SimRepInstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +// +// Functions that track operations on the volume +// + +FLT_PREOP_CALLBACK_STATUS +SimRepPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_PREOP_CALLBACK_STATUS +SimRepPreNetworkQueryOpen ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +// +// Functions to support rename and hard link creation remapping +// + +FLT_PREOP_CALLBACK_STATUS +SimRepPreSetInformation ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +// +// Functions that provide string allocation support +// + +_When_(return==0, _Post_satisfies_(String->Buffer != NULL)) +NTSTATUS +SimRepAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +VOID +SimRepFreeUnicodeString ( + _Inout_ PUNICODE_STRING String + ); + +NTSTATUS +SimRepReplaceFileObjectName ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ); + +BOOLEAN +SimRepCompareMapping( + _In_ PFLT_FILE_NAME_INFORMATION NameInfo, + _In_ PUNICODE_STRING MappingPath, + _In_ BOOLEAN IgnoreCase, + _Out_opt_ PBOOLEAN ExactMatch + ); + +NTSTATUS +SimRepMungeName( + _In_ PFLT_FILE_NAME_INFORMATION NameInfo, + _In_ PUNICODE_STRING SubPath, + _In_ PUNICODE_STRING NewSubPath, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ExactMatch, + _Out_ PUNICODE_STRING MungedPath + ); + +// +// Functions that implement a pass through name provider +// + +NTSTATUS +SimRepGenerateFileName ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _When_(FileObject->FsContext != NULL, _In_opt_) + _When_(FileObject->FsContext == NULL, _In_) + PFLT_CALLBACK_DATA Cbd, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Out_ PBOOLEAN CacheFileNameInformation, + _Inout_ PFLT_NAME_CONTROL FileName + ); + +NTSTATUS +SimRepNormalizeNameComponent ( + _In_ PFLT_INSTANCE Instance, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ); + +#if SIMREP_VISTA +NTSTATUS +SimRepNormalizeNameComponentEx ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ); +#endif + +NTSTATUS +SimRepQueryDirectoryFile ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_writes_bytes_(Length) PVOID FileInformationBuffer, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ); + + +// +// Filter callback routines +// + +FLT_OPERATION_REGISTRATION Callbacks[] = { + + { IRP_MJ_CREATE, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreCreate, + NULL }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreNetworkQueryOpen, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Filter registration data structure +// + +FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + NULL, // Context + Callbacks, // Operation callbacks + SimRepUnload, // Filters unload routine + SimRepInstanceSetup, // InstanceSetup routine + SimRepInstanceQueryTeardown, // InstanceQueryTeardown routine + NULL, // InstanceTeardownStart routine + NULL, // InstanceTeardownComplete routine + NULL, // Filename generation support callback + NULL, // Filename normalization support callback + NULL, // Normalize name component cleanup callback +#if SIMREP_VISTA + NULL, // Transaction notification callback + NULL // Filename normalization support callback + +#endif // SIMREP_VISTA +}; + + +// +// Filter callback routines with rename handling +// + +FLT_OPERATION_REGISTRATION CallbacksWithRename[] = { + + { IRP_MJ_CREATE, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreCreate, + NULL }, + + { IRP_MJ_NETWORK_QUERY_OPEN, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreNetworkQueryOpen, + NULL }, + + { IRP_MJ_SET_INFORMATION, + FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO, + SimRepPreSetInformation, + NULL }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Filter registration data structure with renames +// Filter registers as a name provider and for SetInformation +// + +FLT_REGISTRATION FilterRegistrationWithRename = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + NULL, // Context + CallbacksWithRename, // Operation callbacks + SimRepUnload, // Filters unload routine + SimRepInstanceSetup, // InstanceSetup routine + SimRepInstanceQueryTeardown, // InstanceQueryTeardown routine + NULL, // InstanceTeardownStart routine + NULL, // InstanceTeardownComplete routine + SimRepGenerateFileName, // Filename generation support callback + SimRepNormalizeNameComponent, // Filename normalization support callback + NULL, // Normalize name component cleanup callback +#if SIMREP_VISTA + NULL, // Transaction notification callback + SimRepNormalizeNameComponentEx // Filename normalization support callback + +#endif // SIMREP_VISTA +}; + + +// +// Global variables +// + +SIMREP_GLOBAL_DATA Globals; + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, SimRepSetConfiguration) +#pragma alloc_text(PAGE, SimRepUnload) +#pragma alloc_text(PAGE, SimRepInstanceSetup) +#pragma alloc_text(PAGE, SimRepInstanceQueryTeardown) +#pragma alloc_text(PAGE, SimRepAllocateUnicodeString) +#pragma alloc_text(PAGE, SimRepFreeUnicodeString) +#pragma alloc_text(PAGE, SimRepReplaceFileObjectName) +#pragma alloc_text(PAGE, SimRepCompareMapping) +#pragma alloc_text(PAGE, SimRepMungeName) +#pragma alloc_text(PAGE, SimRepPreCreate) +#pragma alloc_text(PAGE, SimRepPreNetworkQueryOpen) +#pragma alloc_text(PAGE, SimRepPreSetInformation) +#pragma alloc_text(PAGE, SimRepFreeGlobals) +#pragma alloc_text(PAGE, SimRepGenerateFileName) +#pragma alloc_text(PAGE, SimRepNormalizeNameComponent) +#if SIMREP_VISTA +#pragma alloc_text(PAGE, SimRepNormalizeNameComponentEx) +#endif +#pragma alloc_text(PAGE, SimRepQueryDirectoryFile) + +#endif + +// +// Filter driver initialization and unload routines +// + +#pragma warning(push) +#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression + +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; + UNICODE_STRING replaceRoutineName; + PFLT_REGISTRATION Registration; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Set default global configuration + // + +#if DBG + + Globals.DebugLevel = DEBUG_TRACE_ALL; + +#endif + + Globals.RemapRenamesAndLinks = FALSE; + + RtlInitUnicodeString( &Globals.Mapping.NewName, NULL ); + + RtlInitUnicodeString( &Globals.Mapping.OldName, NULL ); + + // + // Import function to replace file names. + // + + RtlInitUnicodeString( &replaceRoutineName, REPLACE_ROUTINE_NAME_STRING ); + + Globals.ReplaceFileNameFunction = MmGetSystemRoutineAddress( &replaceRoutineName ); + if (Globals.ReplaceFileNameFunction == NULL) { + + Globals.ReplaceFileNameFunction = SimRepReplaceFileObjectName; + } + + // + // If available (Windows Vista or later), use the FltQueryDirectoryFile API. + // + + Globals.QueryDirectoryFileFunction = FltGetRoutineAddress( REPLACE_QUERY_DIRECTORY_FILE_ROUTINE_NAME_STRING ); + + // + // Set the filter configuration based on registry keys + // + + status = SimRepSetConfiguration( RegistryPath ); + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[SimRep]: Driver being loaded\n") ); + + if (!NT_SUCCESS( status )) { + + goto DriverEntryCleanup; + } + + // + // Register with the filter manager. If the filter is not + // configured to remap renames and hardlink creation do not + // register name provider or SetInformation callbacks. + // + + Registration = (Globals.RemapRenamesAndLinks == FALSE) ? + &FilterRegistration : &FilterRegistrationWithRename; + + status = FltRegisterFilter( DriverObject, + Registration, + &Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + goto DriverEntryCleanup; + } + + // + // Start filtering I/O + // + + status = FltStartFiltering( Globals.Filter ); + + if (!NT_SUCCESS( status )) { + + FltUnregisterFilter( Globals.Filter ); + } + + +DriverEntryCleanup: + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[SimRep]: Driver loaded complete (Status = 0x%08X)\n", + status) ); + + if (!NT_SUCCESS( status )) { + + SimRepFreeGlobals(); + } + + return status; +} +#pragma warning(pop) + +NTSTATUS +SimRepSetConfiguration( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Descrition: + + This routine sets the filter configuration based on registry values. + +Arguments: + + RegistryPath - The path key passed to the driver during DriverEntry. + +Return Value: + + Returns the status of this operation. + + +--*/ +{ + NTSTATUS status; + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey = NULL; + UNICODE_STRING valueName; + UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; + PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION)buffer; + ULONG valueLength = sizeof(buffer); + ULONG resultLength; + PKEY_VALUE_PARTIAL_INFORMATION mappingValue = NULL; + ULONG mappingValueLength = 0; + WCHAR oldMappingTail; + WCHAR newMappingTail; + + PAGED_CODE(); + + // + // Open the SimRep registry key. + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + +#if DBG + + // + // Query the debug level + // + + RtlInitUnicodeString( &valueName, L"DebugLevel" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.DebugLevel = *(PULONG)value->Data; + } + +#endif + + + // + // Query the remap rename flag + // + + RtlInitUnicodeString( &valueName, L"RemapRenamesAndLinks" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + value, + valueLength, + &resultLength ); + + if (NT_SUCCESS( status )) { + + Globals.RemapRenamesAndLinks = *(PULONG)value->Data > 0 ? TRUE : FALSE; + } + + // + // Query the length of the old mapping. + // + + RtlInitUnicodeString( &valueName, L"OldMapping" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + NULL, + 0, + &mappingValueLength ); + + if (status!=STATUS_BUFFER_TOO_SMALL && status!=STATUS_BUFFER_OVERFLOW) { + + status = STATUS_INVALID_PARAMETER; + goto SimRepSetConfigurationCleanup; + } + + // + // Extract the old mapping string. + // + + mappingValue = ExAllocatePoolWithTag( PagedPool, + mappingValueLength, + SIMREP_REG_TAG ); + + if (mappingValue == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto SimRepSetConfigurationCleanup; + } + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + mappingValue, + mappingValueLength, + &resultLength ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + if (mappingValue->Type != REG_SZ) { + + status = STATUS_INVALID_PARAMETER; + goto SimRepSetConfigurationCleanup; + } + + Globals.Mapping.OldName.MaximumLength = (USHORT)mappingValue->DataLength; + + status = SimRepAllocateUnicodeString( &Globals.Mapping.OldName ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + // + // The length which we receive from ZwQueryValueKey contains size for + // the NULL termination as well. Since we are dealing with unicode + // string we'll chop off the null termination in the length. + // + + Globals.Mapping.OldName.Length = (USHORT)mappingValue->DataLength - sizeof( UNICODE_NULL ); + + RtlCopyMemory(Globals.Mapping.OldName.Buffer, + mappingValue->Data, + Globals.Mapping.OldName.Length); + + // + // Query the length of the new mapping. + // + + RtlInitUnicodeString( &valueName, L"NewMapping" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + mappingValue, + mappingValueLength, + &mappingValueLength ); + + if (!NT_SUCCESS( status )) { + + if (status!=STATUS_BUFFER_TOO_SMALL && status!=STATUS_BUFFER_OVERFLOW) { + + goto SimRepSetConfigurationCleanup; + } + + ExFreePoolWithTag( mappingValue, SIMREP_REG_TAG ); + + mappingValue = ExAllocatePoolWithTag( PagedPool, + mappingValueLength, + SIMREP_REG_TAG ); + + if (mappingValue == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto SimRepSetConfigurationCleanup; + } + + } + + // + // Extract the new mapping string. + // + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + mappingValue, + mappingValueLength, + &mappingValueLength ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + + if (mappingValue->Type != REG_SZ) { + + status = STATUS_INVALID_PARAMETER; + goto SimRepSetConfigurationCleanup; + } + + Globals.Mapping.NewName.MaximumLength = (USHORT) mappingValue->DataLength; + + status = SimRepAllocateUnicodeString( &Globals.Mapping.NewName ); + + if (!NT_SUCCESS( status )) { + + goto SimRepSetConfigurationCleanup; + } + + // + // The length which we receive from ZwQueryValueKey contains size for + // the NULL termination as well. Since we are dealing with unicode + // string we'll chop off the null termination in the length. + // + + Globals.Mapping.NewName.Length = (USHORT)mappingValue->DataLength - sizeof( UNICODE_NULL ); + + RtlCopyMemory(Globals.Mapping.NewName.Buffer, + mappingValue->Data, + Globals.Mapping.NewName.Length); + + + // + // Ensure the old and new mapping are consistent in specifying either files or directories + // as determined by the presence of a trailing backslash + // + + oldMappingTail = (WCHAR)Globals.Mapping.OldName.Buffer[Globals.Mapping.OldName.Length / sizeof( WCHAR ) - 1]; + newMappingTail = (WCHAR)Globals.Mapping.NewName.Buffer[Globals.Mapping.NewName.Length / sizeof( WCHAR ) - 1]; + + if ((oldMappingTail != newMappingTail) && + ((oldMappingTail == OBJ_NAME_PATH_SEPARATOR) || + (newMappingTail == OBJ_NAME_PATH_SEPARATOR))) { + + status = STATUS_INVALID_PARAMETER; + goto SimRepSetConfigurationCleanup; + } + + +SimRepSetConfigurationCleanup: + + if (mappingValue != NULL) { + + ExFreePoolWithTag( mappingValue, SIMREP_REG_TAG ); + mappingValue = NULL; + } + + if (driverRegKey != NULL) { + + ZwClose( driverRegKey ); + } + + if (!NT_SUCCESS( status )) { + + SimRepFreeUnicodeString( &Globals.Mapping.NewName ); + SimRepFreeUnicodeString( &Globals.Mapping.OldName ); + } + + return status; +} + + + +VOID SimRepFreeGlobals( + ) +/*++ + +Routine Descrition: + + This routine cleans up the global structure on both + teardown and initialization failure. + +Arguments: + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + SimRepFreeUnicodeString( &Globals.Mapping.NewName ); + SimRepFreeUnicodeString( &Globals.Mapping.OldName ); +} + +NTSTATUS +SimRepUnload ( + 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. SimRep can unload + easily because it does not own any IOs. When the filter is unloaded + existing reparsed creates will continue to work, but new creates will + not be reparsed. This is fine from the filter's perspective, but could + result in unexpected bahavior for apps. + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + UNREFERENCED_PARAMETER( Flags ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_LOAD_UNLOAD, + ("[SimRep]: Unloading driver\n") ); + + FltUnregisterFilter( Globals.Filter ); + + SimRepFreeGlobals(); + + return STATUS_SUCCESS; +} + + +// +// Instance setup/teardown routines. +// + +NTSTATUS +SimRepInstanceSetup ( + _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. + SimRep does not attach on automatic attachment, but will attach when asked + manually. + +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. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + PAGED_CODE(); + + if ( FlagOn( Flags, FLTFL_INSTANCE_SETUP_AUTOMATIC_ATTACHMENT ) ) { + + // + // Do not automatically attach to a volume. + // + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[Simrep]: Instance setup skipped (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + return STATUS_FLT_DO_NOT_ATTACH; + } + + // + // Attach on manual attachment. + // + + DebugTrace( DEBUG_TRACE_INSTANCES, + ("[SimRep]: Instance setup started (Volume = %p, Instance = %p)\n", + FltObjects->Volume, + FltObjects->Instance) ); + + + return STATUS_SUCCESS; +} + + +NTSTATUS +SimRepInstanceQueryTeardown ( + _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. SimRep only implements it + because otherwise calls to FltDetachVolume or FilterDetach would + fail to detach. + +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( DEBUG_TRACE_INSTANCES, + ("[SimRep]: Instance query teadown ended (Instance = %p)\n", + FltObjects->Instance) ); + + return STATUS_SUCCESS; +} + + +FLT_PREOP_CALLBACK_STATUS +SimRepPreNetworkQueryOpen ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Because network query opens are FastIo operations, they cannot be reparsed. + This means network query opens which need to be redirected must be failed + with FLT_PREOP_DISALLOW_FASTIO. This will cause the Io Manager to reissue + the open as a regular IRP based open. To prevent performance regression, + only fail network query opens which need to be reparsed. + + This is pageable because it can not be called on the paging path + +Arguments: + + Cbd - 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. + +--*/ +{ + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + NTSTATUS status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + BOOLEAN match; + PIO_STACK_LOCATION irpSp; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepQueryOpen -> 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 + + // + // We only registered for this IRP, so thats all we better get! + // + + NT_ASSERT( Cbd->Iopb->MajorFunction == IRP_MJ_NETWORK_QUERY_OPEN ); + NT_ASSERT( FLT_IS_FASTIO_OPERATION( Cbd ) ); + + irpSp = IoGetCurrentIrpStackLocation(Cbd->Iopb->Parameters.NetworkQueryOpen.Irp); + + // + // Check if this is a paging file as we don't want to redirect + // the location of the paging file. + // + + if (FlagOn( irpSp->Flags, SL_OPEN_PAGING_FILE )) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Ignoring paging file open (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreNetworkQueryOpenCleanup; + } + + // + // We are not allowing volume opens to be reparsed in the sample. + // + + if (FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN )) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Ignoring volume open (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreNetworkQueryOpenCleanup; + + } + + // + // Don't reparse an open by ID because it is not possible to determine create path intent. + // + + if (FlagOn( irpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID )) { + + goto SimRepPreNetworkQueryOpenCleanup; + } + + // + // A rename should never come on the fast IO path + // + + NT_ASSERT( irpSp->Flags != SL_OPEN_TARGET_DIRECTORY ); + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Failed to get name information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreNetworkQueryOpenCleanup; + } + + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Processing create for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + // + // Parse the filename information + // + + status = FltParseFileNameInformation( nameInfo ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Failed to parse name information for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreNetworkQueryOpenCleanup; + } + + // + // Determine if this query involes a path that matches the remapping path. + // Note: if the create is case sensitive this comparison must be as well. + // + + match = SimRepCompareMapping( nameInfo, + &Globals.Mapping.OldName, + !FlagOn( irpSp->Flags, SL_CASE_SENSITIVE ), + NULL ); + + if (match) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS, + ("[SimRep]: SimRepPreNetworkQueryOpen -> File name %wZ matches mapping. (Cbd = %p, FileObject = %p)\n" + "\tMapping.OldFileName = %wZ\n" + "\tMapping.NewFileName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + Globals.Mapping.OldName, + Globals.Mapping.NewName) ); + + // + // Because the file matched the mapping, we need to redirect this open with a new name. + // + + // + // We can't return STATUS_REPARSE because it is FastIO. Return + // FLT_PREOP_DISALLOW_FASTIO, so it will be reissued down the slow path. + // + + DebugTrace(DEBUG_TRACE_REPARSED_REISSUE, + ("[SimRep]: Disallow fast IO that is to a mapped path! %wZ\n", + &nameInfo->Name) ); + + callbackStatus = FLT_PREOP_DISALLOW_FASTIO; + + } + + +SimRepPreNetworkQueryOpenCleanup: + + // + // Release the references we have acquired + // + + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (!NT_SUCCESS( status )) { + + // + // An error occurred, fail the query + // + + DebugTrace( DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed with status 0x%x \n", + status) ); + + Cbd->IoStatus.Status = status; + callbackStatus = FLT_PREOP_COMPLETE; + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreNetworkQueryOpen -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + +FLT_PREOP_CALLBACK_STATUS +SimRepPreCreate ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine does the work for SimRep sample. SimRepPreCreate is called in + the pre-operation path for IRP_MJ_CREATE and IRP_MJ_NETWORK_QUERY_OPEN. + The function queries the requested file name for the create and compares + it to the mapping path. If the file is down the "old mapping path", the + filter checks to see if the request is fast io based. If it is we cannot + reparse the create because fast io does not support STATUS_REPARSE. + Instead we return FLT_PREOP_DISALLOW_FASTIO to force the io to be reissued + on the IRP path. If the create is IRP based, then we replace the file + object's file name field with a new path based on the "new mapping path". + + This is pageable because it could not be called on the paging path + +Arguments: + + Cbd - 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. + +--*/ +{ + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + NTSTATUS status; + FLT_PREOP_CALLBACK_STATUS callbackStatus; + UNICODE_STRING newFileName; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( CompletionContext ); + + PAGED_CODE(); + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreCreate -> 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 + + RtlInitUnicodeString( &newFileName, NULL ); + + // + // We only registered for this irp, so thats all we better get! + // + + NT_ASSERT( Cbd->Iopb->MajorFunction == IRP_MJ_CREATE ); + + // + // Check if this is a paging file as we don't want to redirect + // the location of the paging file. + // + + if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_PAGING_FILE )) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreCreate -> Ignoring paging file open (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreCreateCleanup; + } + + // + // We are not allowing volume opens to be reparsed in the sample. + // + + if (FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN )) { + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreCreate -> Ignoring volume open (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreCreateCleanup; + + } + + // + // SimRep does not honor the FILE_OPEN_REPARSE_POINT create option. For a + // symbolic the caller would pass this flag, for example, in order to open + // the link for deletion. There is no concept of deleting the mapping for + // this filter so it is not clear what the purpose of honoring this flag + // would be. + // + + // + // Don't reparse an open by ID because it is not possible to determine create path intent. + // + + if (FlagOn( Cbd->Iopb->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID )) { + + goto SimRepPreCreateCleanup; + } + + if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ) && + !Globals.RemapRenamesAndLinks) { + + // + // This is a prelude to a rename or hard link creation but the filter + // is NOT configured to filter these operations. To perform the operation + // successfully and in a consistent manner this create must not trigger + // a reparse. Pass through the create without attempting any redirection. + // + + goto SimRepPreCreateCleanup; + + } + + // + // Get the name information. + // + + if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY )) { + + // + // The SL_OPEN_TARGET_DIRECTORY flag indicates the caller is attempting + // to open the target of a rename or hard link creation operation. We + // must clear this flag when asking fltmgr for the name or the result + // will not include the final component. We need the full path in order + // to compare the name to our mapping. + // + + ClearFlag( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ); + + DebugTrace( DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS, + ("[SimRep]: SimRepPreCreate -> Clearing SL_OPEN_TARGET_DIRECTORY for %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + + // + // Get the filename as it appears below this filter. Note that we use + // FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY when querying the filename + // so that the filename as it appears below this filter does not end up + // in filter manager's name cache. + // + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY, + &nameInfo ); + + // + // Restore the SL_OPEN_TARGET_DIRECTORY flag so the create will proceed + // for the target. The file systems depend on this flag being set in + // the target create in order for the subsequent SET_INFORMATION + // operation to proceed correctly. + // + + SetFlag( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ); + + + } else { + + // + // Note that we use FLT_FILE_NAME_QUERY_DEFAULT when querying the + // filename. In the precreate the filename should not be in filter + // manager's name cache so there is no point looking there. + // + + status = FltGetFileNameInformation( Cbd, + FLT_FILE_NAME_OPENED | + FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + } + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed to get name information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreCreateCleanup; + } + + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS, + ("[SimRep]: SimRepPreCreate -> Processing create for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + // + // Parse the filename information + // + + status = FltParseFileNameInformation( nameInfo ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed to parse name information for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreCreateCleanup; + } + + // + // Munge the path from the old mapping to new mapping if the query overlaps + // the mapping path. Note: if the create is case sensitive this comparison + // must be as well. + // + + status = SimRepMungeName( nameInfo, + &Globals.Mapping.OldName, + &Globals.Mapping.NewName, + !FlagOn( Cbd->Iopb->OperationFlags, SL_CASE_SENSITIVE ), + FALSE, + &newFileName); + + if (!NT_SUCCESS( status )) { + + if (status == STATUS_NOT_FOUND) { + status = STATUS_SUCCESS; + } + + goto SimRepPreCreateCleanup; + } + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS, + ("[SimRep]: SimRepPreCreate -> File name %wZ matches mapping. (Cbd = %p, FileObject = %p)\n" + "\tMapping.OldFileName = %wZ\n" + "\tMapping.NewFileName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + Globals.Mapping.OldName, + Globals.Mapping.NewName) ); + + + // + // Switch names + // + + status = Globals.ReplaceFileNameFunction( Cbd->Iopb->TargetFileObject, + newFileName.Buffer, + newFileName.Length ); + + if ( !NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed to allocate string for file %wZ (Cbd = %p, FileObject = %p)\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject )); + + goto SimRepPreCreateCleanup; + } + + // + // Set the status to STATUS_REPARSE + // + + status = STATUS_REPARSE; + + + DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_REPARSED_OPERATIONS, + ("[SimRep]: SimRepPreCreate -> Returning STATUS_REPARSE for file %wZ. (Cbd = %p, FileObject = %p)\n" + "\tNewName = %wZ\n", + &nameInfo->Name, + Cbd, + FltObjects->FileObject, + &newFileName) ); + +SimRepPreCreateCleanup: + + // + // Release the references we have acquired + // + + SimRepFreeUnicodeString( &newFileName ); + + if (nameInfo != NULL) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (status == STATUS_REPARSE) { + + // + // Reparse the open + // + + Cbd->IoStatus.Status = STATUS_REPARSE; + Cbd->IoStatus.Information = IO_REPARSE; + callbackStatus = FLT_PREOP_COMPLETE; + + } else if (!NT_SUCCESS( status )) { + + // + // An error occurred, fail the open + // + + DebugTrace( DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreCreate -> Failed with status 0x%x \n", + status) ); + + Cbd->IoStatus.Status = status; + callbackStatus = FLT_PREOP_COMPLETE; + } + + DebugTrace( DEBUG_TRACE_ALL_IO, + ("[SimRep]: SimRepPreCreate -> Exit (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + return callbackStatus; + +} + + +FLT_PREOP_CALLBACK_STATUS +SimRepPreSetInformation ( + _Inout_ PFLT_CALLBACK_DATA Cbd, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + Pre callback for handling SetInformation. + +Arguments: + + Cdb - 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. + +--*/ +{ + FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + NTSTATUS status = STATUS_SUCCESS; + PVOID buffer = NULL; + ULONG bufferLength = 0; + FILE_INFORMATION_CLASS fileInfoClass; + PFILE_RENAME_INFORMATION renameInfo = NULL; + PFILE_RENAME_INFORMATION newRenameInfo = NULL; + PFILE_LINK_INFORMATION linkInfo = NULL; + PFILE_LINK_INFORMATION newLinkInfo = NULL; + PFLT_FILE_NAME_INFORMATION nameInfo = NULL; + UNICODE_STRING newFileName; + + struct { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + PWSTR FileName; + } setInfo; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( CompletionContext ); + + RtlInitUnicodeString(&newFileName, NULL); + + NT_ASSERT( Globals.RemapRenamesAndLinks ); + + fileInfoClass = Cbd->Iopb->Parameters.SetFileInformation.FileInformationClass; + + switch (fileInfoClass) { + + case FileRenameInformation: + + // + // Note: We should never see a rename of the mapping path \x\y itself + // because the name would have been reparsed to the new mapping \a\b. + // This is different than the behavior of normal reparse points where + // the same operation would reassign the reparse point. + // + + renameInfo = Cbd->Iopb->Parameters.SetFileInformation.InfoBuffer; + + setInfo.ReplaceIfExists = renameInfo->ReplaceIfExists; + setInfo.RootDirectory = renameInfo->RootDirectory; + setInfo.FileNameLength = renameInfo->FileNameLength; + setInfo.FileName = renameInfo->FileName; + + break; + + case FileLinkInformation: + + linkInfo = Cbd->Iopb->Parameters.SetFileInformation.InfoBuffer; + + setInfo.ReplaceIfExists = linkInfo->ReplaceIfExists; + setInfo.RootDirectory = linkInfo->RootDirectory; + setInfo.FileNameLength = linkInfo->FileNameLength; + setInfo.FileName = linkInfo->FileName; + + break; + + case FileDirectoryInformation: // 1 + case FileFullDirectoryInformation: // 2 + case FileBothDirectoryInformation: // 3 + case FileBasicInformation: // 4 wdm + case FileStandardInformation: // 5 wdm + case FileInternalInformation: // 6 + case FileEaInformation: // 7 + case FileAccessInformation: // 8 + case FileNameInformation: // 9 + case FileNamesInformation: // 12 + case FileDispositionInformation: // 13 + case FilePositionInformation: // 14 wdm + case FileFullEaInformation: // 15 + case FileModeInformation: // 16 + case FileAlignmentInformation: // 17 + case FileAllInformation: // 18 + case FileAllocationInformation: // 19 + case FileEndOfFileInformation: // 20 wdm + case FileAlternateNameInformation: // 21 + case FileStreamInformation: // 22 + case FilePipeInformation: // 23 + case FilePipeLocalInformation: // 24 + case FilePipeRemoteInformation: // 25 + case FileMailslotQueryInformation: // 26 + case FileMailslotSetInformation: // 27 + case FileCompressionInformation: // 28 + case FileObjectIdInformation: // 29 + case FileCompletionInformation: // 30 + case FileMoveClusterInformation: // 31 + case FileQuotaInformation: // 32 + case FileReparsePointInformation: // 33 + case FileNetworkOpenInformation: // 34 + case FileAttributeTagInformation: // 35 + case FileTrackingInformation: // 36 + case FileIdBothDirectoryInformation: // 37 + case FileIdFullDirectoryInformation: // 38 + case FileValidDataLengthInformation: // 39 + case FileShortNameInformation: // 40 + + goto SimRepPreSetInformationCleanup; + + default: + + // + // It is risky to pass through information classes that we don't + // know about. Try to catch new or invalid classes in testing. + // + + NT_ASSERTMSG("SimRep passing through unknown information class\n", FALSE); + goto SimRepPreSetInformationCleanup; + } + + // + // When this filter is configured to remap renames and hardlinks we need + // to ensure other filters see a consistent destination for the + // operation. The FileName buffer will not match the actual rename path + // when a reparse is involved and if lower filters pass it to + // FltGetDestinationFileNameInformation they will get back the wrong + // destination. To fix this we'll need to munge the FileName buffer + // explicitly. + // + // The reason FltGetDestinationFileNameInformation gives the correct + // destination here is because we send it to ourselves (the current + // provider) and, as a name provider, our filter will get the creates + // issued for the parent directory name normalization and perform the + // reparse. + // + + status = FltGetDestinationFileNameInformation( FltObjects->Instance, + FltObjects->FileObject, + setInfo.RootDirectory, + setInfo.FileName, + setInfo.FileNameLength, + FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER | FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, + &nameInfo ); + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreSetInformation -> Failed to get destination filename information (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreSetInformationCleanup; + } + + status = FltParseFileNameInformation( nameInfo ); + + if (!NT_SUCCESS( status )) { + + goto SimRepPreSetInformationCleanup; + } + + // + // Stream operations are already consistent regardless of whether the file + // is redirected so there is nothing to do. + // + + if (nameInfo->Stream.Length != 0) { + + goto SimRepPreSetInformationCleanup; + } + + // + // If the operation destion overlaps the new mapping get a new filename + // string to send in the request. + // + + status = SimRepMungeName( nameInfo, + &Globals.Mapping.NewName, + &Globals.Mapping.NewName, + !FlagOn( FltObjects->FileObject->Flags, FO_OPENED_CASE_SENSITIVE ), + FALSE, + &newFileName ); + + if (status == STATUS_NOT_FOUND) { + + // + // If the operation destination overlaps the old mapping exactly, get + // a new filename string munged with the new mapping to send in the + // request. This is a special case where our name provider will not + // perform the reparse during name resolution because the parent + // directories don't overlap the mapping. + // + + status = SimRepMungeName( nameInfo, + &Globals.Mapping.OldName, + &Globals.Mapping.NewName, + !FlagOn( FltObjects->FileObject->Flags, FO_OPENED_CASE_SENSITIVE ), + TRUE, + &newFileName ); + } + + if (!NT_SUCCESS( status )) { + + // + // The rename doesn't overlap the mapping at all. No need to munge + // + + if (status == STATUS_NOT_FOUND) { + status = STATUS_SUCCESS; + } + + goto SimRepPreSetInformationCleanup; + } + + // + // Explicitly set the munged the name in the set information structure so + // lower filters who see this operation will see the correct + // destination from FLT_GET_DESTINATION_FILE_NAME_INFORMATION. + // + + if (fileInfoClass == FileRenameInformation) { + + bufferLength = FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ) + newFileName.Length; + + buffer = ExAllocatePoolWithTag( PagedPool, SIMREP_STRING_TAG, bufferLength ); + + if (buffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto SimRepPreSetInformationCleanup; + } + + newRenameInfo = (PFILE_RENAME_INFORMATION)buffer; + + newRenameInfo->ReplaceIfExists = renameInfo->ReplaceIfExists; + newRenameInfo->RootDirectory = NULL; + newRenameInfo->FileNameLength = newFileName.Length; + + RtlCopyMemory( &newRenameInfo->FileName, newFileName.Buffer, newFileName.Length ); + + } else if (fileInfoClass == FileLinkInformation) { + + bufferLength = FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ) + newFileName.Length; + + buffer = ExAllocatePoolWithTag( PagedPool, SIMREP_STRING_TAG, bufferLength ); + + if (buffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto SimRepPreSetInformationCleanup; + } + + newLinkInfo = (PFILE_LINK_INFORMATION)buffer; + + newLinkInfo->ReplaceIfExists = linkInfo->ReplaceIfExists; + newLinkInfo->RootDirectory = NULL; + newLinkInfo->FileNameLength = newFileName.Length; + + RtlCopyMemory( &newLinkInfo->FileName, newFileName.Buffer, newFileName.Length ); + + } + + status = FltSetInformationFile( FltObjects->Instance, + FltObjects->FileObject, + buffer, + bufferLength, + fileInfoClass ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS | DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepPreSetInformation -> Failed sending FltSetInformationFile (Cbd = %p, FileObject = %p)\n", + Cbd, + FltObjects->FileObject) ); + + goto SimRepPreSetInformationCleanup; + } + + Cbd->IoStatus.Status = status; + + returnStatus = FLT_PREOP_COMPLETE; + + +SimRepPreSetInformationCleanup: + + if (nameInfo) { + + FltReleaseFileNameInformation( nameInfo ); + } + + if (buffer) { + + ExFreePoolWithTag( buffer, SIMREP_STRING_TAG ); + } + + SimRepFreeUnicodeString( &newFileName ); + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[SimRep]: SimRepSetInformation -> Failed with status 0x%x \n", + status) ); + + Cbd->IoStatus.Status = status; + + returnStatus = FLT_PREOP_COMPLETE; + } + + return returnStatus; + +} + + +// +// Support Routines +// + +_When_(return==0, _Post_satisfies_(String->Buffer != NULL)) +NTSTATUS +SimRepAllocateUnicodeString ( + _Inout_ PUNICODE_STRING String + ) +/*++ + +Routine Description: + + This routine allocates a unicode string + +Arguments: + + Size - the size in bytes needed for the string buffer + + 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( NonPagedPool, + String->MaximumLength, + SIMREP_STRING_TAG ); + + if (String->Buffer == NULL) { + + DebugTrace( DEBUG_TRACE_ERROR, + ("[SimRep]: Failed to allocate unicode string of size 0x%x\n", + String->MaximumLength) ); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + String->Length = 0; + + return STATUS_SUCCESS; +} + + +VOID +SimRepFreeUnicodeString ( + _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(); + + if (String->Buffer) { + + ExFreePoolWithTag( String->Buffer, + SIMREP_STRING_TAG ); + String->Buffer = NULL; + } + + String->Length = String->MaximumLength = 0; + String->Buffer = NULL; +} + + +NTSTATUS +SimRepReplaceFileObjectName ( + _In_ PFILE_OBJECT FileObject, + _In_reads_bytes_(FileNameLength) PWSTR NewFileName, + _In_ USHORT FileNameLength + ) +/*++ +Routine Description: + + This routine is used to replace a file object's name + with a provided name. This should only be called if + IoReplaceFileObjectName is not on the system. + If this function is used and verifier is enabled + the filter will fail to unload due to a false + positive on the leaked pool test. + +Arguments: + + FileObject - Pointer to file object whose name is to be replaced. + + NewFileName - Pointer to buffer containing the new name. + + FileNameLength - Length of the new name in bytes. + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES - No memory to allocate the new buffer. + + STATUS_SUCCESS otherwise. + +--*/ +{ + PWSTR buffer; + PUNICODE_STRING fileName; + USHORT newMaxLength; + + PAGED_CODE(); + + fileName = &FileObject->FileName; + + // + // If the new name fits inside the current buffer we simply copy it over + // instead of allocating a new buffer (and keep the MaximumLength value + // the same). + // + if (FileNameLength <= fileName->MaximumLength) { + + goto CopyAndReturn; + } + + // + // Use an optimal buffer size + // + newMaxLength = FileNameLength; + + buffer = ExAllocatePoolWithTag( PagedPool, + newMaxLength, + SIMREP_STRING_TAG ); + + if (!buffer) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (fileName->Buffer != NULL) { + + ExFreePool(fileName->Buffer); + } + + fileName->Buffer = buffer; + fileName->MaximumLength = newMaxLength; + +CopyAndReturn: + + fileName->Length = FileNameLength; + RtlZeroMemory(fileName->Buffer, fileName->MaximumLength); + RtlCopyMemory(fileName->Buffer, NewFileName, FileNameLength); + + return STATUS_SUCCESS; +} + + +NTSTATUS +SimRepMungeName( + _In_ PFLT_FILE_NAME_INFORMATION NameInfo, + _In_ PUNICODE_STRING SubPath, + _In_ PUNICODE_STRING NewSubPath, + _In_ BOOLEAN IgnoreCase, + _In_ BOOLEAN ExactMatch, + _Out_ PUNICODE_STRING MungedPath + ) +/*++ +Routine Description: + + This routine will create a new path by munginging a new subpath + over and existing subpath. + +Arguments: + + NameInfo - Pointer to the name information for the file. + + SubPath - The path to munge. + + IgnoreCase - If TRUE do a case insenstive comparison. + + ExactMatch - If TRUE only proceed if the whole path will be replaced + + MungedPath - A unicode string to received the munged path created. The + buffer of the string will be allocated in this function. + +Return Value: + + STATUS_SUCCESS - the path was successfully munged + STATUS_NOT_FOUND - the SubPath was not found or is not an exact match + An appropriate NTSTATUS error otherwise. + +--*/ +{ + NTSTATUS status = STATUS_NOT_FOUND; + BOOLEAN match; + BOOLEAN exactMatch; + USHORT length; + + PAGED_CODE(); + + match = SimRepCompareMapping( NameInfo, SubPath, IgnoreCase, &exactMatch ); + + if (match) { + + if (ExactMatch && !exactMatch) { + + goto SimRepMungeNameCleanup; + } + + NT_ASSERT( NameInfo->Name.Length >= SubPath->Length ); + + length = NameInfo->Name.Length - SubPath->Length + NewSubPath->Length; + + RtlInitUnicodeString( MungedPath, NULL ); + + MungedPath->MaximumLength = (USHORT)length; + + status = SimRepAllocateUnicodeString( MungedPath ); + + if (!NT_SUCCESS( status )) { + + goto SimRepMungeNameCleanup; + } + + // + // Copy the volume portion of the name (part of the name preceding the matching part) + // + + RtlCopyUnicodeString( MungedPath, &NameInfo->Volume ); + + // + // Copy the new file name in place of the matching part of the name + // + + status = RtlAppendUnicodeStringToString( MungedPath, NewSubPath ); + + NT_ASSERT( NT_SUCCESS( status ) ); + + // + // Copy the portion of the name following the matching part of the name + // + + RtlCopyMemory( Add2Ptr( MungedPath->Buffer, NameInfo->Volume.Length + NewSubPath->Length ), + Add2Ptr( NameInfo->Name.Buffer, NameInfo->Volume.Length + SubPath->Length ), + NameInfo->Name.Length - NameInfo->Volume.Length - SubPath->Length ); + + // + // Compute the final length of the new name + // + + MungedPath->Length = length; + + } + +SimRepMungeNameCleanup: + + return status; +} + +BOOLEAN +SimRepCompareMapping( + _In_ PFLT_FILE_NAME_INFORMATION NameInfo, + _In_ PUNICODE_STRING MappingPath, + _In_ BOOLEAN IgnoreCase, + _Out_opt_ PBOOLEAN ExactMatch + ) +/*++ +Routine Description: + + This routine will compare the file specified by the + name information structure to the given mapping path + to determine if the file is the mapping path itself + or a child of the mapping path. + +Arguments: + + NameInfo - Pointer to the name information for the file. + + MappingPath - The mapping path to compare against. + + IgnoreCase - If TRUE do a case insenstive comparison. + + ExactMatch - If supplied receives TRUE if the name exactly + matches the mapping path. + +Return Value: + + TRUE - the file matches the mapping path + + FALSE - the file is not in the mapping path + +--*/ +{ + UNICODE_STRING fileName; + BOOLEAN match; + BOOLEAN exactMatch; + + PAGED_CODE(); + + // + // The NameInfo parameter is assumed to have been parsed + // + + NT_ASSERT (FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_FINAL_COMPONENT) && + FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_EXTENSION) && + FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_STREAM) && + FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_PARENT_DIR)); + + // + // Point filename to the name of the file, excluding the name of the volume + // + + NT_ASSERT( NameInfo->Name.Buffer == NameInfo->Volume.Buffer ); + NT_ASSERT( NameInfo->Name.Length >= NameInfo->Volume.Length); + + match = FALSE; + exactMatch = FALSE; + fileName.Buffer = Add2Ptr( NameInfo->Name.Buffer, NameInfo->Volume.Length ); + fileName.MaximumLength = NameInfo->Name.Length - NameInfo->Volume.Length; + fileName.Length = fileName.MaximumLength; + + // + // Check if the filename matches this mapping entry (is the mapping + // entry itself or some child directory of the mapping entry) + // + + if (RtlPrefixUnicodeString( MappingPath, &fileName, IgnoreCase )) { + + if (fileName.Length == MappingPath->Length) { + + // + // This path is the mapping itself + // + + match = TRUE; + + exactMatch = TRUE; + + } else if (fileName.Buffer[(MappingPath->Length/sizeof( WCHAR ))] == OBJ_NAME_PATH_SEPARATOR) { + + // + // This path is a child of the mapping + // + + match = TRUE; + } + + // + // No match here means the path simply overlaps the mapping like + // \a\b\c overlaps \a\b\cd.txt + // + + } + + if (ARGUMENT_PRESENT( ExactMatch )) { + *ExactMatch = exactMatch; + } + + return match; +} + + +// +// In order to remap renames and hard links correctly SimRep needs +// to be called as part of name resolution. To achieve this SimRep +// must be a name provider, albeit a simple "pass through" provider. +// SimRep is is only demonstrating how to simulate reparse points, +// not how to virtualize a namespace. Hence the name provider does +// not munge the names but simply passes the name queries through. +// + +NTSTATUS +SimRepGenerateFileName ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _When_(FileObject->FsContext != NULL, _In_opt_) + _When_(FileObject->FsContext == NULL, _In_) + PFLT_CALLBACK_DATA Cbd, + _In_ FLT_FILE_NAME_OPTIONS NameOptions, + _Out_ PBOOLEAN CacheFileNameInformation, + _Inout_ PFLT_NAME_CONTROL FileName + ) +/*++ + +Routine Description: + + This routine generates a file name of the type specified in NameFormat + for the specified file object. + +Arguments: + + Instance - Opaque instance pointer for the minifilter driver instance that + this callback routine is registered for. + + FileObject - The fileobject for which the name is being requested. + + Cbd - If non-NULL, the CallbackData structure defining the operation + we are in the midst of processing when this name is queried. + + NameOptions - value that specifies the name format, query method, and flags + for this file name information query + + CacheFileNameInformation - A pointer to a Boolean value specifying whether + this name can be cached. + + FileName - A pointer to a filter manager-allocated FLT_NAME_CONTROL + structure to receive the file name on output + +Return Value: + + Returns STATUS_SUCCESS if a name could be returned, or the appropriate + error otherwise. + +--*/ +{ + PFLT_FILE_NAME_INFORMATION userFileNameInfo = NULL; + PUNICODE_STRING userFileName; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + + // + // Clear FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER from the name options + // We pass the same name options when we issue a name query to satisfy this + // name query. We want that name query to be targeted below simrep.sys and + // not recurse into simrep.sys + // + + ClearFlag( NameOptions, FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER ); + + if (FileObject->FsContext == NULL) { + + + // + // This file object has not yet been opened. We will query the filter + // manager for the name and return that name. We must use the original + // NameOptions we received in the query. If we were to swallow flags + // such as FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY or + // FLT_FILE_NAME_DO_NOT_CACHE we could corrupt the name cache. + // + + status = FltGetFileNameInformation( Cbd, + NameOptions, + &userFileNameInfo ); + + if (!NT_SUCCESS( status )) { + + goto SimRepGenerateFileNameCleanup; + } + + userFileName = &userFileNameInfo->Name; + + } else { + + // + // The file has been opened. If the call is not in the context of an IO + // operation (we don't have a callback data), we have to get the + // filename with FltGetFilenameInformationUnsafe using the fileobject. + // Note, the only way we won't have a callback is if someone called + // FltGetFileNameInformationUnsafe already. + // + + if (ARGUMENT_PRESENT( Cbd )) { + + status = FltGetFileNameInformation( Cbd, + NameOptions, + &userFileNameInfo ); + + } else { + + status = FltGetFileNameInformationUnsafe( FileObject, + Instance, + NameOptions, + &userFileNameInfo ); + + } + + if (!NT_SUCCESS( status )) { + + goto SimRepGenerateFileNameCleanup; + } + + userFileName = &userFileNameInfo->Name; + + } + + status = FltCheckAndGrowNameControl( FileName, + userFileName->Length ); + + if (!NT_SUCCESS( status )) { + + goto SimRepGenerateFileNameCleanup; + } + + RtlCopyUnicodeString( &FileName->Name, userFileName ); + + // + // If the file object is unopened then the name of the stream represented by + // the file object may change from pre-create to post-create. + // For example, the name being opened could actually be a symbolic link + // + + *CacheFileNameInformation = (FileObject->FsContext != NULL); + + +SimRepGenerateFileNameCleanup: + + if (userFileNameInfo != NULL) { + + FltReleaseFileNameInformation( userFileNameInfo ); + } + + if (!NT_SUCCESS( status )) { + + DebugTrace( DEBUG_TRACE_NAME_OPERATIONS | DEBUG_TRACE_ERROR, + ("SimRepGenerateFileName: failed %x\n", + status) ); + } + + return status; +} + + +NTSTATUS +SimRepNormalizeNameComponent ( + _In_ PFLT_INSTANCE Instance, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ) +/*++ + +Routine Description: + + This routine normalizes, converts to a long name if needed, a name component. + +Arguments: + + Instance - Opaque instance pointer for the minifilter driver instance that + this callback routine is registered for. + + ParentDirectory - Pointer to a UNICODE_STRING structure that contains the + name of the parent directory for this name component. + + VolumeNameLength - Length, in bytes, of the parent directory name that is + stored in the structure that the ParentDirectory parameter points to. + + Component - Pointer to a UNICODE_STRING structure that contains the name + component to be expanded. + + ExpandComponentName - Pointer to a FILE_NAMES_INFORMATION structure that + receives the expanded (normalized) file name information for the name component. + + ExpandComponentNameLength - Length, in bytes, of the buffer that the + ExpandComponentName parameter points to. + + Flags - Name normalization flags. + + NormalizationContext - Pointer to minifilter driver-provided context + information to be passed in any subsequent calls to this callback routine + that are made to normalize the remaining components in the same file name + path. + +Return Value: + + Returns STATUS_SUCCESS if a name could be returned, or the appropriate + error otherwise. + +--*/ +{ + NTSTATUS status; + HANDLE directoryHandle = NULL; + PFILE_OBJECT directoryFileObject = NULL; + OBJECT_ATTRIBUTES objAttributes; + IO_STATUS_BLOCK ioStatusBlock; + BOOLEAN ignoreCase = !BooleanFlagOn( Flags, + FLTFL_NORMALIZE_NAME_CASE_SENSITIVE ); + + UNREFERENCED_PARAMETER( NormalizationContext ); + UNREFERENCED_PARAMETER( DeviceNameLength ); + + PAGED_CODE(); + + // + // Validate the buffer is big enough + // + + if (ExpandComponentNameLength < sizeof(FILE_NAMES_INFORMATION)) { + + return STATUS_INVALID_PARAMETER; + } + + InitializeObjectAttributes( &objAttributes, + (PUNICODE_STRING)ParentDirectory, + OBJ_KERNEL_HANDLE + | (ignoreCase ? OBJ_CASE_INSENSITIVE : 0), + NULL, + NULL ); + + status = FltCreateFile( Globals.Filter, + Instance, + &directoryHandle, + FILE_LIST_DIRECTORY | SYNCHRONIZE, // DesiredAccess + &objAttributes, + &ioStatusBlock, + NULL, // AllocationSize + FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_NORMAL, // FileAttributes + FILE_SHARE_READ + | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, // ShareAccess + FILE_OPEN, // CreateDisposition + FILE_DIRECTORY_FILE + | FILE_SYNCHRONOUS_IO_NONALERT + | FILE_OPEN_FOR_BACKUP_INTENT, // CreateOptions + NULL, // EaBuffer + 0, // EaLength + IO_IGNORE_SHARE_ACCESS_CHECK ); // Flags + + if (!NT_SUCCESS( status )) { + + goto SimRepNormalizeNameComponentCleanup; + } + + status = ObReferenceObjectByHandle( directoryHandle, + FILE_LIST_DIRECTORY | SYNCHRONIZE, // DesiredAccess + *IoFileObjectType, + KernelMode, + &directoryFileObject, + NULL ); + + + if (!NT_SUCCESS( status )) { + + goto SimRepNormalizeNameComponentCleanup; + } + + // + // Query the file entry to get the long name + // + + status = SimRepQueryDirectoryFile( Instance, + directoryFileObject, + ExpandComponentName, + ExpandComponentNameLength, + FileNamesInformation, + TRUE, /* ReturnSingleEntry */ + (PUNICODE_STRING)Component, + TRUE, /* restartScan */ + NULL ); + + +SimRepNormalizeNameComponentCleanup: + + + if (NULL != directoryHandle) { + + FltClose( directoryHandle ); + } + + if (NULL != directoryFileObject) { + + ObDereferenceObject( directoryFileObject ); + } + + return status; + +} + + +#if SIMREP_VISTA +NTSTATUS +SimRepNormalizeNameComponentEx ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _In_ PCUNICODE_STRING ParentDirectory, + _In_ USHORT DeviceNameLength, + _In_ PCUNICODE_STRING Component, + _Out_writes_bytes_(ExpandComponentNameLength) PFILE_NAMES_INFORMATION ExpandComponentName, + _In_ ULONG ExpandComponentNameLength, + _In_ FLT_NORMALIZE_NAME_FLAGS Flags, + _Inout_ PVOID *NormalizationContext + ) +/*++ + +Routine Description: + + This routine normalizes, converts to a long name if needed, a name component. + +Arguments: + + Instance - Opaque instance pointer for the minifilter driver instance that + this callback routine is registered for. + + FileObject - Pointer to the file object for the file whose name is being + requested or the file that is the target of the IRP_MJ_SET_INFORMATION + operation if the FLTFL_NORMALIZE_NAME_DESTINATION_FILE_NAME flag is set. + ee the Flags parameter below for more information. + + ParentDirectory - Pointer to a UNICODE_STRING structure that contains the + name of the parent directory for this name component. + + VolumeNameLength - Length, in bytes, of the parent directory name that is + stored in the structure that the ParentDirectory parameter points to. + + Component - Pointer to a UNICODE_STRING structure that contains the name + component to be expanded. + + ExpandComponentName - Pointer to a FILE_NAMES_INFORMATION structure that + receives the expanded (normalized) file name information for the name component. + + ExpandComponentNameLength - Length, in bytes, of the buffer that the + ExpandComponentName parameter points to. + + Flags - Name normalization flags. + + NormalizationContext - Pointer to minifilter driver-provided context + information to be passed in any subsequent calls to this callback routine + that are made to normalize the remaining components in the same file name + path. + +Return Value: + + Returns STATUS_SUCCESS if a name could be returned, or the appropriate + error otherwise. + +--*/ +{ + NTSTATUS status; + HANDLE directoryHandle = NULL; + PFILE_OBJECT directoryFileObject = NULL; + OBJECT_ATTRIBUTES objAttributes; + IO_STATUS_BLOCK ioStatusBlock; + BOOLEAN ignoreCase = !BooleanFlagOn( Flags, + FLTFL_NORMALIZE_NAME_CASE_SENSITIVE ); + IO_DRIVER_CREATE_CONTEXT createContext; + TXN_PARAMETER_BLOCK txnBlock; + PTXN_PARAMETER_BLOCK originalTxnBlock; + + UNREFERENCED_PARAMETER( NormalizationContext ); + UNREFERENCED_PARAMETER( DeviceNameLength ); + + PAGED_CODE(); + + // + // Validate the buffer is big enough + // + + if (ExpandComponentNameLength < sizeof(FILE_NAMES_INFORMATION)) { + + return STATUS_INVALID_PARAMETER; + } + + InitializeObjectAttributes( &objAttributes, + (PUNICODE_STRING)ParentDirectory, + OBJ_KERNEL_HANDLE + | (ignoreCase ? OBJ_CASE_INSENSITIVE : 0), + NULL, + NULL ); + + ASSERT( ARGUMENT_PRESENT( FileObject ) ); + + // + // On Vista and beyond, we need to query the normalized name in the context + // of the same transaction as the name query + // + + IoInitializeDriverCreateContext( &createContext ); + + originalTxnBlock = IoGetTransactionParameterBlock( FileObject ); + + if (originalTxnBlock != NULL) { + + // + // Do not propagate the miniversion for the parent open + // as directories don't have a miniversion. + // + + txnBlock.Length = sizeof( txnBlock ); + txnBlock.TransactionObject = originalTxnBlock->TransactionObject; + txnBlock.TxFsContext = TXF_MINIVERSION_DEFAULT_VIEW; + + createContext.TxnParameters = &txnBlock; + } + + status = FltCreateFileEx2( Globals.Filter, + Instance, + &directoryHandle, + &directoryFileObject, + FILE_LIST_DIRECTORY | SYNCHRONIZE, // DesiredAccess + &objAttributes, + &ioStatusBlock, + NULL, // AllocationSize + FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_NORMAL, // FileAttributes + FILE_SHARE_READ + | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, // ShareAccess + FILE_OPEN, // CreateDisposition + FILE_DIRECTORY_FILE + | FILE_SYNCHRONOUS_IO_NONALERT + | FILE_OPEN_FOR_BACKUP_INTENT, // CreateOptions + NULL, // EaBuffer + 0, // EaLength + IO_IGNORE_SHARE_ACCESS_CHECK, // Flags + &createContext ); + + + if (!NT_SUCCESS( status )) { + + goto SimRepNormalizeNameComponentExCleanup; + } + + // + // Query the file entry to get the long name + // + + status = SimRepQueryDirectoryFile( Instance, + directoryFileObject, + ExpandComponentName, + ExpandComponentNameLength, + FileNamesInformation, + TRUE, /* ReturnSingleEntry */ + (PUNICODE_STRING)Component, + TRUE, /* restartScan */ + NULL ); + + +SimRepNormalizeNameComponentExCleanup: + + + if (NULL != directoryHandle) { + + FltClose( directoryHandle ); + } + + if (NULL != directoryFileObject) { + + ObDereferenceObject( directoryFileObject ); + } + + return status; +} +#endif + +NTSTATUS +SimRepQueryDirectoryFile ( + _In_ PFLT_INSTANCE Instance, + _In_ PFILE_OBJECT FileObject, + _Out_writes_bytes_(Length) PVOID FileInformationBuffer, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan, + _Out_opt_ PULONG LengthReturned + ) +/*++ + +Routine Description: + + This function is like ZwQueryDirectoryFile for filters + +Arguments: + + Instance - Supplies the Instance initiating this IO. + + FileObject - Supplies the file object about which the requested + information should be changed. + + FileInformation - Supplies a buffer containing the information which should + be changed on the file. + + Length - Supplies the length, in bytes, of the FileInformation buffer. + + FileInformationClass - Specifies the type of information which should be + changed about the file. + + ReturnSingleEntry - If this parameter is TRUE, SimRepQueryDirectoryFile + returns only the first entry that is found. + + FileName - An optional pointer to a caller-allocated Unicode string + containing the name of a file (or multiple files, if wildcards are used) + within the directory specified by FileHandle. This parameter is optional + and can be NULL. + + RestartScan - Set to TRUE if the scan is to start at the first entry in + the directory. Set to FALSE if resuming the scan from a previous call. + + +Return Value: + + The status returned is the final completion status of the operation. + +--*/ + +{ + PFLT_CALLBACK_DATA data; + NTSTATUS status; + + PAGED_CODE(); + + if (Globals.QueryDirectoryFileFunction != NULL) { + + return Globals.QueryDirectoryFileFunction( Instance, + FileObject, + FileInformationBuffer, + Length, + FileInformationClass, + ReturnSingleEntry, + FileName, + RestartScan, + LengthReturned ); + } + + // + // Customized FltQueryDirectoryFile if it is not exported from FltMgr. + // + + status = FltAllocateCallbackData( Instance, FileObject, &data ); + + if (!NT_SUCCESS( status )) { + + return status; + } + + data->Iopb->MajorFunction = IRP_MJ_DIRECTORY_CONTROL; + data->Iopb->MinorFunction = IRP_MN_QUERY_DIRECTORY; + + data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length = Length; + data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName = FileName; + data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass = FileInformationClass; + data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileIndex = 0; + + data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer = FileInformationBuffer; + data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress = NULL; + + if (RestartScan) { + + data->Iopb->OperationFlags |= SL_RESTART_SCAN; + } + + if (ReturnSingleEntry) { + + data->Iopb->OperationFlags |= SL_RETURN_SINGLE_ENTRY; + } + + // + // Perform a synchronous operation. + // + + FltPerformSynchronousIo( data ); + + status = data->IoStatus.Status; + + if (ARGUMENT_PRESENT(LengthReturned) && + NT_SUCCESS( status )) { + + *LengthReturned = (ULONG) data->IoStatus.Information; + } + + FltFreeCallbackData( data ); + + return status; +} + + + diff --git a/filesys/miniFilter/simrep/simrep.inf b/filesys/miniFilter/simrep/simrep.inf new file mode 100644 index 00000000..ba9f8af9 --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.inf @@ -0,0 +1,110 @@ +;;; +;;; Simulate Reparse File System Filter Driver Sample +;;; +;;; +;;; Copyright (c) 1999 - 2001, Microsoft Corporation +;;; + +[Version] +Signature = "$Windows NT$" +Class = "ActivityMonitor" ;This is determined by the work this filter driver does +ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} +Provider = %Msft% +DriverVer = 01/01/2004,1.0.0.1 +CatalogFile = simrep.cat + +[DestinationDirs] +DefaultDestDir = 12 +SimRep.DriverFiles = 12 ;%windir%\system32\drivers + +[SourceDisksNames] +1 = %Disk1% + +[SourceDisksFiles] +simrep.sys = 1 + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %SimRepServiceDesc% +CopyFiles = SimRep.DriverFiles + +[DefaultInstall.Services] +AddService = %SimRepServiceName%,,SimRep.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = SimRep.DriverFiles +DelReg = SimRep.DelRegistry + + +[DefaultUninstall.Services] +DelService = SimRep,0x204 + +; +; Services Section +; + +[SimRep.Service] +DisplayName = %SimRepServiceName% +Description = %SimRepServiceDesc% +ServiceBinary = %12%\simrep.sys ;%windir%\system32\drivers\simrep.sys +Dependencies = %FltmgrServiceName% ;FltMgr +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Activity Monitor" +AddReg = SimRep.AddRegistry + +; +; Registry Modifications +; + +[SimRep.AddRegistry] +HKR,,%SimRepDebugLevel%,0x00010001,0x1F +HKR,,%SimRepRemapRenamesAndLinks%,0x00010001,0x00 +HKR,,"NewMapping",0x00000000,%NewMapping% +HKR,,"OldMapping",0x00000000,%OldMapping% +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,%RegInstancesSubkeyName%,%RegDefaultInstanceValueName%,0x00000000,%DefaultInstance% +HKR,%RegInstancesSubkeyName%"\"%Instance1.Name%,%RegAltitudeValueName%,0x00000000,%Instance1.Altitude% +HKR,%RegInstancesSubkeyName%"\"%Instance1.Name%,%RegFlagsValueName%,0x00010001,%Instance1.Flags% + +[SimRep.DelRegistry] + +; +; Copy Files +; + +[SimRep.DriverFiles] +simrep.sys + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +SimRepServiceDesc = "Simulate Reparse File System Filter Driver Sample" +SimRepServiceName = "SimRep" +SimRepDebugLevel = "DebugLevel" +SimRepRemapRenamesAndLinks = "RemapRenamesAndLinks" +FltmgrServiceName = "FltMgr" +RegInstancesSubkeyName = "Instances" +RegDefaultInstanceValueName = "DefaultInstance" +RegAltitudeValueName = "Altitude" +RegFlagsValueName = "Flags" +Disk1 = "SimRep Source Media" +NewMapping = "\a\b" +OldMapping = "\x\y" + +;Instances specific information. +DefaultInstance = "SimRep" +Instance1.Name = "SimRep" +Instance1.Altitude = "371100" +Instance1.Flags = 0x0 diff --git a/filesys/miniFilter/simrep/simrep.rc b/filesys/miniFilter/simrep/simrep.rc new file mode 100644 index 00000000..a9475aae --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Simulate Reparse Sample Mini-Filter" +#define VER_INTERNALNAME_STR "simrep.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/simrep/simrep.sln b/filesys/miniFilter/simrep/simrep.sln new file mode 100644 index 00000000..179a1e3c --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simrep", "simrep.vcxproj", "{8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|Win32.ActiveCfg = Debug|Win32 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|Win32.Build.0 = Debug|Win32 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|Win32.ActiveCfg = Release|Win32 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|Win32.Build.0 = Release|Win32 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|x64.ActiveCfg = Debug|x64 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Debug|x64.Build.0 = Debug|x64 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|x64.ActiveCfg = Release|x64 + {8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/simrep/simrep.vcxproj b/filesys/miniFilter/simrep/simrep.vcxproj new file mode 100644 index 00000000..41f903f0 --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.vcxproj @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{8CA1286B-6F29-4AF8-90C9-C6CD8B5F9B6B}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{E65EC005-19A7-464F-8CA6-C0A78FD9F8C8}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>simrep</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>simrep</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>simrep</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>simrep</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="SimRep.c" /> + <ResourceCompile Include="SimRep.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/simrep/simrep.vcxproj.Filters b/filesys/miniFilter/simrep/simrep.vcxproj.Filters new file mode 100644 index 00000000..e5b8f9ad --- /dev/null +++ b/filesys/miniFilter/simrep/simrep.vcxproj.Filters @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{D5037E9C-B07A-4282-8BD3-F0F1852E3411}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{793B13DB-7C30-4334-A398-EA998A473206}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{65B4A597-DA92-4875-BC82-E7B93DBCAA49}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{37180E6D-5204-4358-98A7-E348F7EBB425}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="SimRep.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="SimRep.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/swapBuffers/ReadMe.md b/filesys/miniFilter/swapBuffers/ReadMe.md new file mode 100644 index 00000000..da19b5ed --- /dev/null +++ b/filesys/miniFilter/swapBuffers/ReadMe.md @@ -0,0 +1,15 @@ +SwapBuffer File System Minifilter Driver +======================================== + +The SwapBuffers minifilter demonstrates how to switch buffers between reads and writes of data. This technique is particularly useful for encryption filters because they have to encrypt data before writing it to disk and decrypt it after reading it from disk. Because encryption/decryption has to be done transparently, you cannot use system-supplied buffers directly, so intermediate buffers have to be introduced. + +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. + +Design and Operation +-------------------- + +The *SwapBuffers* minifilter introduces a new buffer before a read/write or directory control operations. The corresponding operation is then performed on the new buffer instead of the buffer that was originally provided. After the operation completes, the contents of the new buffer are copied back in to the original buffer. + +For more information on file system minifilter design, start with the [File System Minifilter Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540402) section in the Installable File Systems Design Guide. + diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.c b/filesys/miniFilter/swapBuffers/swapBuffers.c new file mode 100644 index 00000000..47893870 --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.c @@ -0,0 +1,2334 @@ +/*++ + +Copyright (c) 1999 - 2002 Microsoft Corporation + +Module Name: + + SwapBuffers.c + +Abstract: + + This is a sample filter which demonstrates proper access of data buffer + and a general guideline of how to swap buffers. + For now it only swaps buffers for: + + IRP_MJ_READ + IRP_MJ_WRITE + IRP_MJ_DIRECTORY_CONTROL + + By default this filter attaches to all volumes it is notified about. It + does support having multiple instances on a given volume. + +Environment: + + Kernel mode + +--*/ + +#include <fltKernel.h> +#include <dontuse.h> +#include <suppress.h> + +#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers") + + +PFLT_FILTER gFilterHandle; + +/************************************************************************* + Pool Tags +*************************************************************************/ + +#define BUFFER_SWAP_TAG 'bdBS' +#define CONTEXT_TAG 'xcBS' +#define NAME_TAG 'mnBS' +#define PRE_2_POST_TAG 'ppBS' + +/************************************************************************* + Local structures +*************************************************************************/ + +// +// This is a volume context, one of these are attached to each volume +// we monitor. This is used to get a "DOS" name for debug display. +// + +typedef struct _VOLUME_CONTEXT { + + // + // Holds the name to display + // + + UNICODE_STRING Name; + + // + // Holds the sector size for this volume. + // + + ULONG SectorSize; + +} VOLUME_CONTEXT, *PVOLUME_CONTEXT; + +#define MIN_SECTOR_SIZE 0x200 + + +// +// This is a context structure that is used to pass state from our +// pre-operation callback to our post-operation callback. +// + +typedef struct _PRE_2_POST_CONTEXT { + + // + // Pointer to our volume context structure. We always get the context + // in the preOperation path because you can not safely get it at DPC + // level. We then release it in the postOperation path. It is safe + // to release contexts at DPC level. + // + + PVOLUME_CONTEXT VolCtx; + + // + // Since the post-operation parameters always receive the "original" + // parameters passed to the operation, we need to pass our new destination + // buffer to our post operation routine so we can free it. + // + + PVOID SwappedBuffer; + +} PRE_2_POST_CONTEXT, *PPRE_2_POST_CONTEXT; + +// +// This is a lookAside list used to allocate our pre-2-post structure. +// + +NPAGED_LOOKASIDE_LIST Pre2PostContextList; + +/************************************************************************* + Prototypes +*************************************************************************/ + +NTSTATUS +InstanceSetup ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_SETUP_FLAGS Flags, + _In_ DEVICE_TYPE VolumeDeviceType, + _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType + ); + +VOID +CleanupVolumeContext( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ); + +NTSTATUS +InstanceQueryTeardown ( + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags + ); + +DRIVER_INITIALIZE DriverEntry; +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +NTSTATUS +FilterUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +SwapPreReadBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostReadBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostReadBuffersWhenSafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +SwapPreDirCtrlBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostDirCtrlBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostDirCtrlBuffersWhenSafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +FLT_PREOP_CALLBACK_STATUS +SwapPreWriteBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ); + +FLT_POSTOP_CALLBACK_STATUS +SwapPostWriteBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ); + +VOID +ReadDriverParameters ( + _In_ PUNICODE_STRING RegistryPath + ); + +// +// Assign text sections for each routine. +// + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, InstanceSetup) +#pragma alloc_text(PAGE, CleanupVolumeContext) +#pragma alloc_text(PAGE, InstanceQueryTeardown) +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, ReadDriverParameters) +#pragma alloc_text(PAGE, FilterUnload) +#endif + +// +// Operation we currently care about. +// + +CONST FLT_OPERATION_REGISTRATION Callbacks[] = { + { IRP_MJ_READ, + 0, + SwapPreReadBuffers, + SwapPostReadBuffers }, + + { IRP_MJ_WRITE, + 0, + SwapPreWriteBuffers, + SwapPostWriteBuffers }, + + { IRP_MJ_DIRECTORY_CONTROL, + 0, + SwapPreDirCtrlBuffers, + SwapPostDirCtrlBuffers }, + + { IRP_MJ_OPERATION_END } +}; + +// +// Context definitions we currently care about. Note that the system will +// create a lookAside list for the volume context because an explicit size +// of the context is specified. +// + +CONST FLT_CONTEXT_REGISTRATION ContextNotifications[] = { + + { FLT_VOLUME_CONTEXT, + 0, + CleanupVolumeContext, + sizeof(VOLUME_CONTEXT), + CONTEXT_TAG }, + + { FLT_CONTEXT_END } +}; + +// +// This defines what we want to filter with FltMgr +// + +CONST FLT_REGISTRATION FilterRegistration = { + + sizeof( FLT_REGISTRATION ), // Size + FLT_REGISTRATION_VERSION, // Version + 0, // Flags + + ContextNotifications, // Context + Callbacks, // Operation callbacks + + FilterUnload, // MiniFilterUnload + + InstanceSetup, // InstanceSetup + InstanceQueryTeardown, // InstanceQueryTeardown + NULL, // InstanceTeardownStart + NULL, // InstanceTeardownComplete + + NULL, // GenerateFileName + NULL, // GenerateDestinationFileName + NULL // NormalizeNameComponent + +}; + +/************************************************************************* + Debug tracing information +*************************************************************************/ + +// +// Definitions to display log messages. The registry DWORD entry: +// "hklm\system\CurrentControlSet\Services\Swapbuffers\DebugFlags" defines +// the default state of these logging flags +// + +#define LOGFL_ERRORS 0x00000001 // if set, display error messages +#define LOGFL_READ 0x00000002 // if set, display READ operation info +#define LOGFL_WRITE 0x00000004 // if set, display WRITE operation info +#define LOGFL_DIRCTRL 0x00000008 // if set, display DIRCTRL operation info +#define LOGFL_VOLCTX 0x00000010 // if set, display VOLCTX operation info + +ULONG LoggingFlags = 0; // all disabled by default + +#define LOG_PRINT( _logFlag, _string ) \ + (FlagOn(LoggingFlags,(_logFlag)) ? \ + DbgPrint _string : \ + ((int)0)) + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// +// 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. + + By default we want to attach to all volumes. This routine will try and + get a "DOS" name for the given volume. If it can't, it will try and + get the "NT" name for the volume (which is what happens on network + volumes). If a name is retrieved a volume context will be created with + that name. + +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. + +Return Value: + + STATUS_SUCCESS - attach + STATUS_FLT_DO_NOT_ATTACH - do not attach + +--*/ +{ + PDEVICE_OBJECT devObj = NULL; + PVOLUME_CONTEXT ctx = NULL; + NTSTATUS status = STATUS_SUCCESS; + ULONG retLen; + PUNICODE_STRING workingName; + USHORT size; + UCHAR volPropBuffer[sizeof(FLT_VOLUME_PROPERTIES)+512]; + PFLT_VOLUME_PROPERTIES volProp = (PFLT_VOLUME_PROPERTIES)volPropBuffer; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Flags ); + UNREFERENCED_PARAMETER( VolumeDeviceType ); + UNREFERENCED_PARAMETER( VolumeFilesystemType ); + + try { + + // + // Allocate a volume context structure. + // + + status = FltAllocateContext( FltObjects->Filter, + FLT_VOLUME_CONTEXT, + sizeof(VOLUME_CONTEXT), + NonPagedPool, + &ctx ); + + if (!NT_SUCCESS(status)) { + + // + // We could not allocate a context, quit now + // + + leave; + } + + // + // Always get the volume properties, so I can get a sector size + // + + status = FltGetVolumeProperties( FltObjects->Volume, + volProp, + sizeof(volPropBuffer), + &retLen ); + + if (!NT_SUCCESS(status)) { + + leave; + } + + // + // Save the sector size in the context for later use. Note that + // we will pick a minimum sector size if a sector size is not + // specified. + // + + FLT_ASSERT((volProp->SectorSize == 0) || (volProp->SectorSize >= MIN_SECTOR_SIZE)); + + ctx->SectorSize = max(volProp->SectorSize,MIN_SECTOR_SIZE); + + // + // Init the buffer field (which may be allocated later). + // + + ctx->Name.Buffer = NULL; + + // + // Get the storage device object we want a name for. + // + + status = FltGetDiskDeviceObject( FltObjects->Volume, &devObj ); + + if (NT_SUCCESS(status)) { + + // + // Try and get the DOS name. If it succeeds we will have + // an allocated name buffer. If not, it will be NULL + // + +#pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "Used to maintain compatability with Win 2k") + status = RtlVolumeDeviceToDosName( devObj, &ctx->Name ); + } + + // + // If we could not get a DOS name, get the NT name. + // + + if (!NT_SUCCESS(status)) { + + FLT_ASSERT(ctx->Name.Buffer == NULL); + + // + // Figure out which name to use from the properties + // + + if (volProp->RealDeviceName.Length > 0) { + + workingName = &volProp->RealDeviceName; + + } else if (volProp->FileSystemDeviceName.Length > 0) { + + workingName = &volProp->FileSystemDeviceName; + + } else { + + // + // No name, don't save the context + // + + status = STATUS_FLT_DO_NOT_ATTACH; + leave; + } + + // + // Get size of buffer to allocate. This is the length of the + // string plus room for a trailing colon. + // + + size = workingName->Length + sizeof(WCHAR); + + // + // Now allocate a buffer to hold this name + // + +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "ctx->Name.Buffer will not be leaked because it is freed in CleanupVolumeContext") + ctx->Name.Buffer = ExAllocatePoolWithTag( NonPagedPool, + size, + NAME_TAG ); + if (ctx->Name.Buffer == NULL) { + + status = STATUS_INSUFFICIENT_RESOURCES; + leave; + } + + // + // Init the rest of the fields + // + + ctx->Name.Length = 0; + ctx->Name.MaximumLength = size; + + // + // Copy the name in + // + + RtlCopyUnicodeString( &ctx->Name, + workingName ); + + // + // Put a trailing colon to make the display look good + // + + RtlAppendUnicodeToString( &ctx->Name, + L":" ); + } + + // + // Set the context + // + + status = FltSetVolumeContext( FltObjects->Volume, + FLT_SET_CONTEXT_KEEP_IF_EXISTS, + ctx, + NULL ); + + // + // Log debug info + // + + LOG_PRINT( LOGFL_VOLCTX, + ("SwapBuffers!InstanceSetup: Real SectSize=0x%04x, Used SectSize=0x%04x, Name=\"%wZ\"\n", + volProp->SectorSize, + ctx->SectorSize, + &ctx->Name) ); + + // + // It is OK for the context to already be defined. + // + + if (status == STATUS_FLT_CONTEXT_ALREADY_DEFINED) { + + status = STATUS_SUCCESS; + } + + } finally { + + // + // Always release the context. If the set failed, it will free the + // context. If not, it will remove the reference added by the set. + // Note that the name buffer in the ctx will get freed by the context + // cleanup routine. + // + + if (ctx) { + + FltReleaseContext( ctx ); + } + + // + // Remove the reference added to the device object by + // FltGetDiskDeviceObject. + // + + if (devObj) { + + ObDereferenceObject( devObj ); + } + } + + return status; +} + + +VOID +CleanupVolumeContext( + _In_ PFLT_CONTEXT Context, + _In_ FLT_CONTEXT_TYPE ContextType + ) +/*++ + +Routine Description: + + The given context is being freed. + Free the allocated name buffer if there one. + +Arguments: + + Context - The context being freed + + ContextType - The type of context this is + +Return Value: + + None + +--*/ +{ + PVOLUME_CONTEXT ctx = Context; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ContextType ); + + FLT_ASSERT(ContextType == FLT_VOLUME_CONTEXT); + + if (ctx->Name.Buffer != NULL) { + + ExFreePool(ctx->Name.Buffer); + ctx->Name.Buffer = NULL; + } +} + + +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. We always return it is OK to + detach. + +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: + + Always succeed. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + return STATUS_SUCCESS; +} + + +/************************************************************************* + Initialization and unload routines. +*************************************************************************/ + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This is the initialization routine. This registers with FltMgr and + initializes all 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: + + Status of the operation + +--*/ +{ + NTSTATUS status; + + // + // Default to NonPagedPoolNx for non paged pool allocations where supported. + // + + ExInitializeDriverRuntime( DrvRtPoolNxOptIn ); + + // + // Get debug trace flags + // + + ReadDriverParameters( RegistryPath ); + + // + // Init lookaside list used to allocate our context structure used to + // pass information from out preOperation callback to our postOperation + // callback. + // + + ExInitializeNPagedLookasideList( &Pre2PostContextList, + NULL, + NULL, + 0, + sizeof(PRE_2_POST_CONTEXT), + PRE_2_POST_TAG, + 0 ); + + // + // Register with FltMgr + // + + status = FltRegisterFilter( DriverObject, + &FilterRegistration, + &gFilterHandle ); + + if (! NT_SUCCESS( status )) { + + goto SwapDriverEntryExit; + } + + // + // Start filtering i/o + // + + status = FltStartFiltering( gFilterHandle ); + + if (! NT_SUCCESS( status )) { + + FltUnregisterFilter( gFilterHandle ); + goto SwapDriverEntryExit; + } + +SwapDriverEntryExit: + + if(! NT_SUCCESS( status )) { + + ExDeleteNPagedLookasideList( &Pre2PostContextList ); + } + + return status; +} + + +NTSTATUS +FilterUnload ( + _In_ FLT_FILTER_UNLOAD_FLAGS Flags + ) +/*++ + +Routine Description: + + Called when this mini-filter is about to be unloaded. We unregister + from the FltMgr and then return it is OK to unload + +Arguments: + + Flags - Indicating if this is a mandatory unload. + +Return Value: + + Returns the final status of this operation. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Flags ); + + // + // Unregister from FLT mgr + // + + FltUnregisterFilter( gFilterHandle ); + + // + // Delete lookaside list + // + + ExDeleteNPagedLookasideList( &Pre2PostContextList ); + + return STATUS_SUCCESS; +} + + +/************************************************************************* + MiniFilter callback routines. +*************************************************************************/ + +FLT_PREOP_CALLBACK_STATUS +SwapPreReadBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine demonstrates how to swap buffers for the READ operation. + + Note that it handles all errors by simply not doing the buffer swap. + +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 - Receives the context that will be passed to the + post-operation callback. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - we want a postOpeation callback + FLT_PREOP_SUCCESS_NO_CALLBACK - we don't want a postOperation callback + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_PREOP_CALLBACK_STATUS retValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + PVOID newBuf = NULL; + PMDL newMdl = NULL; + PVOLUME_CONTEXT volCtx = NULL; + PPRE_2_POST_CONTEXT p2pCtx; + NTSTATUS status; + ULONG readLen = iopb->Parameters.Read.Length; + + try { + + // + // If they are trying to read ZERO bytes, then don't do anything and + // we don't need a post-operation callback. + // + + if (readLen == 0) { + + leave; + } + + // + // Get our volume context so we can display our volume name in the + // debug output. + // + + status = FltGetVolumeContext( FltObjects->Filter, + FltObjects->Volume, + &volCtx ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreReadBuffers: Error getting volume context, status=%x\n", + status) ); + + leave; + } + + // + // If this is a non-cached I/O we need to round the length up to the + // sector size for this device. We must do this because the file + // systems do this and we need to make sure our buffer is as big + // as they are expecting. + // + + if (FlagOn(IRP_NOCACHE,iopb->IrpFlags)) { + + readLen = (ULONG)ROUND_TO_SIZE(readLen,volCtx->SectorSize); + } + + // + // Allocate aligned nonPaged memory for the buffer we are swapping + // to. This is really only necessary for noncached IO but we always + // do it here for simplification. If we fail to get the memory, just + // don't swap buffers on this operation. + // + + newBuf = FltAllocatePoolAlignedWithTag( FltObjects->Instance, + NonPagedPool, + (SIZE_T) readLen, + BUFFER_SWAP_TAG ); + if (newBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreReadBuffers: %wZ Failed to allocate %d bytes of memory\n", + &volCtx->Name, + readLen) ); + + leave; + } + + // + // We only need to build a MDL for IRP operations. We don't need to + // do this for a FASTIO operation since the FASTIO interface has no + // parameter for passing the MDL to the file system. + // + + if (FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_IRP_OPERATION)) { + + // + // Allocate a MDL for the new allocated memory. If we fail + // the MDL allocation then we won't swap buffer for this operation + // + + newMdl = IoAllocateMdl( newBuf, + readLen, + FALSE, + FALSE, + NULL ); + + if (newMdl == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreReadBuffers: %wZ Failed to allocate MDL\n", + &volCtx->Name) ); + + leave; + } + + // + // setup the MDL for the non-paged pool we just allocated + // + + MmBuildMdlForNonPagedPool( newMdl ); + } + + // + // We are ready to swap buffers, get a pre2Post context structure. + // We need it to pass the volume context and the allocate memory + // buffer to the post operation callback. + // + + p2pCtx = ExAllocateFromNPagedLookasideList( &Pre2PostContextList ); + + if (p2pCtx == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreReadBuffers: %wZ Failed to allocate pre2Post context structure\n", + &volCtx->Name) ); + + leave; + } + + // + // Log that we are swapping + // + + LOG_PRINT( LOGFL_READ, + ("SwapBuffers!SwapPreReadBuffers: %wZ newB=%p newMdl=%p oldB=%p oldMdl=%p len=%d\n", + &volCtx->Name, + newBuf, + newMdl, + iopb->Parameters.Read.ReadBuffer, + iopb->Parameters.Read.MdlAddress, + readLen) ); + + // + // Update the buffer pointers and MDL address, mark we have changed + // something. + // + + iopb->Parameters.Read.ReadBuffer = newBuf; + iopb->Parameters.Read.MdlAddress = newMdl; + FltSetCallbackDataDirty( Data ); + + // + // Pass state to our post-operation callback. + // + + p2pCtx->SwappedBuffer = newBuf; + p2pCtx->VolCtx = volCtx; + + *CompletionContext = p2pCtx; + + // + // Return we want a post-operation callback + // + + retValue = FLT_PREOP_SUCCESS_WITH_CALLBACK; + + } finally { + + // + // If we don't want a post-operation callback, then cleanup state. + // + + if (retValue != FLT_PREOP_SUCCESS_WITH_CALLBACK) { + + if (newBuf != NULL) { + + FltFreePoolAlignedWithTag( FltObjects->Instance, + newBuf, + BUFFER_SWAP_TAG ); + } + + if (newMdl != NULL) { + + IoFreeMdl( newMdl ); + } + + if (volCtx != NULL) { + + FltReleaseContext( volCtx ); + } + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostReadBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine does postRead buffer swap handling + +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 completion context set in the pre-operation routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING + FLT_POSTOP_MORE_PROCESSING_REQUIRED + +--*/ +{ + PVOID origBuf; + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_POSTOP_CALLBACK_STATUS retValue = FLT_POSTOP_FINISHED_PROCESSING; + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + BOOLEAN cleanupAllocatedBuffer = TRUE; + + // + // This system won't draining an operation with swapped buffers, verify + // the draining flag is not set. + // + + FLT_ASSERT(!FlagOn(Flags, FLTFL_POST_OPERATION_DRAINING)); + + try { + + // + // If the operation failed or the count is zero, there is no data to + // copy so just return now. + // + + if (!NT_SUCCESS(Data->IoStatus.Status) || + (Data->IoStatus.Information == 0)) { + + LOG_PRINT( LOGFL_READ, + ("SwapBuffers!SwapPostReadBuffers: %wZ newB=%p No data read, status=%x, info=%Iu\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Status, + Data->IoStatus.Information) ); + + leave; + } + + // + // We need to copy the read data back into the users buffer. Note + // that the parameters passed in are for the users original buffers + // not our swapped buffers. + // + + if (iopb->Parameters.Read.MdlAddress != NULL) { + + // + // This should be a simple MDL. We don't expect chained MDLs + // this high up the stack + // + + FLT_ASSERT( ((PMDL)iopb->Parameters.Read.MdlAddress)->Next == NULL); + + // + // Since there is a MDL defined for the original buffer, get a + // system address for it so we can copy the data back to it. + // We must do this because we don't know what thread context + // we are in. + // + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.Read.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffers: %wZ Failed to get system address for MDL: %p\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.Read.MdlAddress) ); + + // + // If we failed to get a SYSTEM address, mark that the read + // failed and return. + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + leave; + } + + } else if (FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) || + FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_FAST_IO_OPERATION)) { + + // + // If this is a system buffer, just use the given address because + // it is valid in all thread contexts. + // If this is a FASTIO operation, we can just use the + // buffer (inside a try/except) since we know we are in + // the correct thread context (you can't pend FASTIO's). + // + + origBuf = iopb->Parameters.Read.ReadBuffer; + + } else { + + // + // They don't have a MDL and this is not a system buffer + // or a fastio so this is probably some arbitrary user + // buffer. We can not do the processing at DPC level so + // try and get to a safe IRQL so we can do the processing. + // + + if (FltDoCompletionProcessingWhenSafe( Data, + FltObjects, + CompletionContext, + Flags, + SwapPostReadBuffersWhenSafe, + &retValue )) { + + // + // This operation has been moved to a safe IRQL, the called + // routine will do (or has done) the freeing so don't do it + // in our routine. + // + + cleanupAllocatedBuffer = FALSE; + + } else { + + // + // We are in a state where we can not get to a safe IRQL and + // we do not have a MDL. There is nothing we can do to safely + // copy the data back to the users buffer, fail the operation + // and return. This shouldn't ever happen because in those + // situations where it is not safe to post, we should have + // a MDL. + // + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffers: %wZ Unable to post to a safe IRQL\n", + &p2pCtx->VolCtx->Name) ); + + Data->IoStatus.Status = STATUS_UNSUCCESSFUL; + Data->IoStatus.Information = 0; + } + + leave; + } + + // + // We either have a system buffer or this is a fastio operation + // so we are in the proper context. Copy the data handling an + // exception. + // + + try { + + RtlCopyMemory( origBuf, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information ); + + } except (EXCEPTION_EXECUTE_HANDLER) { + + // + // The copy failed, return an error, failing the operation. + // + + Data->IoStatus.Status = GetExceptionCode(); + Data->IoStatus.Information = 0; + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffers: %wZ Invalid user buffer, oldB=%p, status=%x\n", + &p2pCtx->VolCtx->Name, + origBuf, + Data->IoStatus.Status) ); + } + + } finally { + + // + // If we are supposed to, cleanup the allocated memory and release + // the volume context. The freeing of the MDL (if there is one) is + // handled by FltMgr. + // + + if (cleanupAllocatedBuffer) { + + LOG_PRINT( LOGFL_READ, + ("SwapBuffers!SwapPostReadBuffers: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + FltFreePoolAlignedWithTag( FltObjects->Instance, + p2pCtx->SwappedBuffer, + BUFFER_SWAP_TAG ); + + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostReadBuffersWhenSafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + We had an arbitrary users buffer without a MDL so we needed to get + to a safe IRQL so we could lock it and then copy the data. + +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 - Contains state from our PreOperation callback + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - This is always returned. + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + PVOID origBuf; + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + FLT_ASSERT(Data->IoStatus.Information != 0); + + // + // This is some sort of user buffer without a MDL, lock the user buffer + // so we can access it. This will create a MDL for it. + // + + status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffersWhenSafe: %wZ Could not lock user buffer, oldB=%p, status=%x\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.Read.ReadBuffer, + status) ); + + // + // If we can't lock the buffer, fail the operation + // + + Data->IoStatus.Status = status; + Data->IoStatus.Information = 0; + + } else { + + // + // Get a system address for this buffer. + // + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.Read.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostReadBuffersWhenSafe: %wZ Failed to get system address for MDL: %p\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.Read.MdlAddress) ); + + // + // If we couldn't get a SYSTEM buffer address, fail the operation + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + + } else { + + // + // Copy the data back to the original buffer. Note that we + // don't need a try/except because we will always have a system + // buffer address. + // + + RtlCopyMemory( origBuf, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information ); + } + } + + // + // Free allocated memory and release the volume context + // + + LOG_PRINT( LOGFL_READ, + ("SwapBuffers!SwapPostReadBuffersWhenSafe: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + FltFreePoolAlignedWithTag( FltObjects->Instance, + p2pCtx->SwappedBuffer, + BUFFER_SWAP_TAG ); + + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +SwapPreDirCtrlBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine demonstrates how to swap buffers for the Directory Control + operations. The reason this routine is here is because directory change + notifications are long lived and this allows you to see how FltMgr + handles long lived IRP operations that have swapped buffers when the + mini-filter is unloaded. It does this by canceling the IRP. + + Note that it handles all errors by simply not doing the + buffer swap. + +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 - Receives the context that will be passed to the + post-operation callback. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - we want a postOpeation callback + FLT_PREOP_SUCCESS_NO_CALLBACK - we don't want a postOperation callback + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_PREOP_CALLBACK_STATUS retValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + PVOID newBuf = NULL; + PMDL newMdl = NULL; + PVOLUME_CONTEXT volCtx = NULL; + PPRE_2_POST_CONTEXT p2pCtx; + NTSTATUS status; + + try { + + // + // If they are trying to get ZERO bytes, then don't do anything and + // we don't need a post-operation callback. + // + + if (iopb->Parameters.DirectoryControl.QueryDirectory.Length == 0) { + + leave; + } + + // + // Get our volume context. If we can't get it, just return. + // + + status = FltGetVolumeContext( FltObjects->Filter, + FltObjects->Volume, + &volCtx ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreDirCtrlBuffers: Error getting volume context, status=%x\n", + status) ); + + leave; + } + + // + // Allocate nonPaged memory for the buffer we are swapping to. + // If we fail to get the memory, just don't swap buffers on this + // operation. + // + + newBuf = ExAllocatePoolWithTag( NonPagedPool, + iopb->Parameters.DirectoryControl.QueryDirectory.Length, + BUFFER_SWAP_TAG ); + + if (newBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreDirCtrlBuffers: %wZ Failed to allocate %d bytes of memory.\n", + &volCtx->Name, + iopb->Parameters.DirectoryControl.QueryDirectory.Length) ); + + leave; + } + + // + // Zero the new buffer so as not to potentially expose any sensitive + // data to the user. + // + + RtlZeroMemory( newBuf, iopb->Parameters.DirectoryControl.QueryDirectory.Length ); + + + // + // We need to build a MDL because Directory Control Operations are always IRP operations. + // + + + // + // Allocate a MDL for the new allocated memory. If we fail + // the MDL allocation then we won't swap buffer for this operation + // + + newMdl = IoAllocateMdl( newBuf, + iopb->Parameters.DirectoryControl.QueryDirectory.Length, + FALSE, + FALSE, + NULL ); + + if (newMdl == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreDirCtrlBuffers: %wZ Failed to allocate MDL.\n", + &volCtx->Name) ); + + leave; + } + + // + // setup the MDL for the non-paged pool we just allocated + // + + MmBuildMdlForNonPagedPool( newMdl ); + + // + // We are ready to swap buffers, get a pre2Post context structure. + // We need it to pass the volume context and the allocate memory + // buffer to the post operation callback. + // + + p2pCtx = ExAllocateFromNPagedLookasideList( &Pre2PostContextList ); + + if (p2pCtx == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreDirCtrlBuffers: %wZ Failed to allocate pre2Post context structure\n", + &volCtx->Name) ); + + leave; + } + + // + // Log that we are swapping + // + + LOG_PRINT( LOGFL_DIRCTRL, + ("SwapBuffers!SwapPreDirCtrlBuffers: %wZ newB=%p newMdl=%p oldB=%p oldMdl=%p len=%d\n", + &volCtx->Name, + newBuf, + newMdl, + iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer, + iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress, + iopb->Parameters.DirectoryControl.QueryDirectory.Length) ); + + // + // Update the buffer pointers and MDL address + // + + iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer = newBuf; + iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress = newMdl; + FltSetCallbackDataDirty( Data ); + + // + // Pass state to our post-operation callback. + // + + p2pCtx->SwappedBuffer = newBuf; + p2pCtx->VolCtx = volCtx; + + *CompletionContext = p2pCtx; + + // + // Return we want a post-operation callback + // + + retValue = FLT_PREOP_SUCCESS_WITH_CALLBACK; + + } finally { + + // + // If we don't want a post-operation callback, then cleanup state. + // + + if (retValue != FLT_PREOP_SUCCESS_WITH_CALLBACK) { + + if (newBuf != NULL) { + + ExFreePool( newBuf ); + } + + if (newMdl != NULL) { + + IoFreeMdl( newMdl ); + } + + if (volCtx != NULL) { + + FltReleaseContext( volCtx ); + } + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostDirCtrlBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + This routine does the post Directory Control buffer swap handling. + +Arguments: + + This routine does postRead buffer swap handling + 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 completion context set in the pre-operation routine. + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING + FLT_POSTOP_MORE_PROCESSING_REQUIRED + +--*/ +{ + PVOID origBuf; + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_POSTOP_CALLBACK_STATUS retValue = FLT_POSTOP_FINISHED_PROCESSING; + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + BOOLEAN cleanupAllocatedBuffer = TRUE; + + // + // Verify we are not draining an operation with swapped buffers + // + + FLT_ASSERT(!FlagOn(Flags, FLTFL_POST_OPERATION_DRAINING)); + + try { + + // + // If the operation failed or the count is zero, there is no data to + // copy so just return now. + // + + if (!NT_SUCCESS(Data->IoStatus.Status) || + (Data->IoStatus.Information == 0)) { + + LOG_PRINT( LOGFL_DIRCTRL, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ newB=%p No data read, status=%x, info=%Ix\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Status, + Data->IoStatus.Information) ); + + leave; + } + + // + // We need to copy the read data back into the users buffer. Note + // that the parameters passed in are for the users original buffers + // not our swapped buffers + // + + if (iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress != NULL) { + + // + // There is a MDL defined for the original buffer, get a + // system address for it so we can copy the data back to it. + // We must do this because we don't know what thread context + // we are in. + // + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ Failed to get system address for MDL: %p\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress) ); + + // + // If we failed to get a SYSTEM address, mark that the + // operation failed and return. + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + leave; + } + + } else if (FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) || + FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_FAST_IO_OPERATION)) { + + // + // If this is a system buffer, just use the given address because + // it is valid in all thread contexts. + // If this is a FASTIO operation, we can just use the + // buffer (inside a try/except) since we know we are in + // the correct thread context. + // + + origBuf = iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; + + } else { + + // + // They don't have a MDL and this is not a system buffer + // or a fastio so this is probably some arbitrary user + // buffer. We can not do the processing at DPC level so + // try and get to a safe IRQL so we can do the processing. + // + + if (FltDoCompletionProcessingWhenSafe( Data, + FltObjects, + CompletionContext, + Flags, + SwapPostDirCtrlBuffersWhenSafe, + &retValue )) { + + // + // This operation has been moved to a safe IRQL, the called + // routine will do (or has done) the freeing so don't do it + // in our routine. + // + + cleanupAllocatedBuffer = FALSE; + + } else { + + // + // We are in a state where we can not get to a safe IRQL and + // we do not have a MDL. There is nothing we can do to safely + // copy the data back to the users buffer, fail the operation + // and return. This shouldn't ever happen because in those + // situations where it is not safe to post, we should have + // a MDL. + // + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ Unable to post to a safe IRQL\n", + &p2pCtx->VolCtx->Name) ); + + Data->IoStatus.Status = STATUS_UNSUCCESSFUL; + Data->IoStatus.Information = 0; + } + + leave; + } + + // + // We either have a system buffer or this is a fastio operation + // so we are in the proper context. Copy the data handling an + // exception. + // + // NOTE: Due to a bug in FASTFAT where it is returning the wrong + // length in the information field (it is sort) we are always + // going to copy the original buffer length. Please note that + // this is a potential security problem because we will copy + // more than what was touched by the FS. So we have to make + // sure the buffer is clean before calling into the FS or we + // risk exposing sensitive data to the user. + // + + try { + + RtlCopyMemory( origBuf, + p2pCtx->SwappedBuffer, + /*Data->IoStatus.Information*/ + iopb->Parameters.DirectoryControl.QueryDirectory.Length ); + + } except (EXCEPTION_EXECUTE_HANDLER) { + + Data->IoStatus.Status = GetExceptionCode(); + Data->IoStatus.Information = 0; + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ Invalid user buffer, oldB=%p, status=%x, info=%Iu\n", + &p2pCtx->VolCtx->Name, + origBuf, + Data->IoStatus.Status, + Data->IoStatus.Information) ); + } + + } finally { + + // + // If we are supposed to, cleanup the allocate memory and release + // the volume context. The freeing of the MDL (if there is one) is + // handled by FltMgr. + // + + if (cleanupAllocatedBuffer) { + + LOG_PRINT( LOGFL_DIRCTRL, + ("SwapBuffers!SwapPostDirCtrlBuffers: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + ExFreePool( p2pCtx->SwappedBuffer ); + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostDirCtrlBuffersWhenSafe ( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + We had an arbitrary users buffer without a MDL so we needed to get + to a safe IRQL so we could lock it and then copy the data. + +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 buffer we allocated and swapped to + + Flags - Denotes whether the completion is successful or is being drained. + +Return Value: + + FLT_POSTOP_FINISHED_PROCESSING - This is always returned. + +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + PVOID origBuf; + NTSTATUS status; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + FLT_ASSERT(Data->IoStatus.Information != 0); + + // + // This is some sort of user buffer without a MDL, lock the + // user buffer so we can access it + // + + status = FltLockUserBuffer( Data ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffersWhenSafe: %wZ Could not lock user buffer, oldB=%p, status=%x\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer, + status) ); + + // + // If we can't lock the buffer, fail the operation + // + + Data->IoStatus.Status = status; + Data->IoStatus.Information = 0; + + } else { + + // + // Get a system address for this buffer. + // + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPostDirCtrlBuffersWhenSafe: %wZ Failed to get System address for MDL: %p\n", + &p2pCtx->VolCtx->Name, + iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress) ); + + // + // If we couldn't get a SYSTEM buffer address, fail the operation + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + + } else { + + // + // Copy the data back to the original buffer + // + // NOTE: Due to a bug in FASTFAT where it is returning the wrong + // length in the information field (it is short) we are + // always going to copy the original buffer length. + // + + RtlCopyMemory( origBuf, + p2pCtx->SwappedBuffer, + /*Data->IoStatus.Information*/ + iopb->Parameters.DirectoryControl.QueryDirectory.Length ); + } + } + + // + // Free the memory we allocated and return + // + + LOG_PRINT( LOGFL_DIRCTRL, + ("SwapBuffers!SwapPostDirCtrlBuffersWhenSafe: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + ExFreePool( p2pCtx->SwappedBuffer ); + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +FLT_PREOP_CALLBACK_STATUS +SwapPreWriteBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _Flt_CompletionContext_Outptr_ PVOID *CompletionContext + ) +/*++ + +Routine Description: + + This routine demonstrates how to swap buffers for the WRITE operation. + + Note that it handles all errors by simply not doing the buffer swap. + +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 - Receives the context that will be passed to the + post-operation callback. + +Return Value: + + FLT_PREOP_SUCCESS_WITH_CALLBACK - we want a postOpeation callback + FLT_PREOP_SUCCESS_NO_CALLBACK - we don't want a postOperation callback + FLT_PREOP_COMPLETE - +--*/ +{ + PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb; + FLT_PREOP_CALLBACK_STATUS retValue = FLT_PREOP_SUCCESS_NO_CALLBACK; + PVOID newBuf = NULL; + PMDL newMdl = NULL; + PVOLUME_CONTEXT volCtx = NULL; + PPRE_2_POST_CONTEXT p2pCtx; + PVOID origBuf; + NTSTATUS status; + ULONG writeLen = iopb->Parameters.Write.Length; + + try { + + // + // If they are trying to write ZERO bytes, then don't do anything and + // we don't need a post-operation callback. + // + + if (writeLen == 0) { + + leave; + } + + // + // Get our volume context so we can display our volume name in the + // debug output. + // + + status = FltGetVolumeContext( FltObjects->Filter, + FltObjects->Volume, + &volCtx ); + + if (!NT_SUCCESS(status)) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: Error getting volume context, status=%x\n", + status) ); + + leave; + } + + // + // If this is a non-cached I/O we need to round the length up to the + // sector size for this device. We must do this because the file + // systems do this and we need to make sure our buffer is as big + // as they are expecting. + // + + if (FlagOn(IRP_NOCACHE,iopb->IrpFlags)) { + + writeLen = (ULONG)ROUND_TO_SIZE(writeLen,volCtx->SectorSize); + } + + // + // Allocate aligned nonPaged memory for the buffer we are swapping + // to. This is really only necessary for noncached IO but we always + // do it here for simplification. If we fail to get the memory, just + // don't swap buffers on this operation. + // + + newBuf = FltAllocatePoolAlignedWithTag( FltObjects->Instance, + NonPagedPool, + (SIZE_T) writeLen, + BUFFER_SWAP_TAG ); + + if (newBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Failed to allocate %d bytes of memory.\n", + &volCtx->Name, + writeLen) ); + + leave; + } + + // + // We only need to build a MDL for IRP operations. We don't need to + // do this for a FASTIO operation because it is a waste of time since + // the FASTIO interface has no parameter for passing the MDL to the + // file system. + // + + if (FlagOn(Data->Flags,FLTFL_CALLBACK_DATA_IRP_OPERATION)) { + + // + // Allocate a MDL for the new allocated memory. If we fail + // the MDL allocation then we won't swap buffer for this operation + // + + newMdl = IoAllocateMdl( newBuf, + writeLen, + FALSE, + FALSE, + NULL ); + + if (newMdl == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Failed to allocate MDL.\n", + &volCtx->Name) ); + + leave; + } + + // + // setup the MDL for the non-paged pool we just allocated + // + + MmBuildMdlForNonPagedPool( newMdl ); + } + + // + // If the users original buffer had a MDL, get a system address. + // + + if (iopb->Parameters.Write.MdlAddress != NULL) { + + // + // This should be a simple MDL. We don't expect chained MDLs + // this high up the stack + // + + FLT_ASSERT( ((PMDL)iopb->Parameters.Write.MdlAddress)->Next == NULL); + + origBuf = MmGetSystemAddressForMdlSafe( iopb->Parameters.Write.MdlAddress, + NormalPagePriority | MdlMappingNoExecute ); + + if (origBuf == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Failed to get system address for MDL: %p\n", + &volCtx->Name, + iopb->Parameters.Write.MdlAddress) ); + + // + // If we could not get a system address for the users buffer, + // then we are going to fail this operation. + // + + Data->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Data->IoStatus.Information = 0; + retValue = FLT_PREOP_COMPLETE; + leave; + } + + } else { + + // + // There was no MDL defined, use the given buffer address. + // + + origBuf = iopb->Parameters.Write.WriteBuffer; + } + + // + // Copy the memory, we must do this inside the try/except because we + // may be using a users buffer address + // + + try { + + RtlCopyMemory( newBuf, + origBuf, + writeLen ); + + } except (EXCEPTION_EXECUTE_HANDLER) { + + // + // The copy failed, return an error, failing the operation. + // + + Data->IoStatus.Status = GetExceptionCode(); + Data->IoStatus.Information = 0; + retValue = FLT_PREOP_COMPLETE; + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Invalid user buffer, oldB=%p, status=%x\n", + &volCtx->Name, + origBuf, + Data->IoStatus.Status) ); + + leave; + } + + // + // We are ready to swap buffers, get a pre2Post context structure. + // We need it to pass the volume context and the allocate memory + // buffer to the post operation callback. + // + + p2pCtx = ExAllocateFromNPagedLookasideList( &Pre2PostContextList ); + + if (p2pCtx == NULL) { + + LOG_PRINT( LOGFL_ERRORS, + ("SwapBuffers!SwapPreWriteBuffers: %wZ Failed to allocate pre2Post context structure\n", + &volCtx->Name) ); + + leave; + } + + // + // Set new buffers + // + + LOG_PRINT( LOGFL_WRITE, + ("SwapBuffers!SwapPreWriteBuffers: %wZ newB=%p newMdl=%p oldB=%p oldMdl=%p len=%d\n", + &volCtx->Name, + newBuf, + newMdl, + iopb->Parameters.Write.WriteBuffer, + iopb->Parameters.Write.MdlAddress, + writeLen) ); + + iopb->Parameters.Write.WriteBuffer = newBuf; + iopb->Parameters.Write.MdlAddress = newMdl; + FltSetCallbackDataDirty( Data ); + + // + // Pass state to our post-operation callback. + // + + p2pCtx->SwappedBuffer = newBuf; + p2pCtx->VolCtx = volCtx; + + *CompletionContext = p2pCtx; + + // + // Return we want a post-operation callback + // + + retValue = FLT_PREOP_SUCCESS_WITH_CALLBACK; + + } finally { + + // + // If we don't want a post-operation callback, then free the buffer + // or MDL if it was allocated. + // + + if (retValue != FLT_PREOP_SUCCESS_WITH_CALLBACK) { + + if (newBuf != NULL) { + + FltFreePoolAlignedWithTag( FltObjects->Instance, + newBuf, + BUFFER_SWAP_TAG ); + + } + + if (newMdl != NULL) { + + IoFreeMdl( newMdl ); + } + + if (volCtx != NULL) { + + FltReleaseContext( volCtx ); + } + } + } + + return retValue; +} + + +FLT_POSTOP_CALLBACK_STATUS +SwapPostWriteBuffers( + _Inout_ PFLT_CALLBACK_DATA Data, + _In_ PCFLT_RELATED_OBJECTS FltObjects, + _In_ PVOID CompletionContext, + _In_ FLT_POST_OPERATION_FLAGS Flags + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ +{ + PPRE_2_POST_CONTEXT p2pCtx = CompletionContext; + + UNREFERENCED_PARAMETER( FltObjects ); + UNREFERENCED_PARAMETER( Flags ); + + LOG_PRINT( LOGFL_WRITE, + ("SwapBuffers!SwapPostWriteBuffers: %wZ newB=%p info=%Iu Freeing\n", + &p2pCtx->VolCtx->Name, + p2pCtx->SwappedBuffer, + Data->IoStatus.Information) ); + + // + // Free allocate POOL and volume context + // + + FltFreePoolAlignedWithTag( FltObjects->Instance, + p2pCtx->SwappedBuffer, + BUFFER_SWAP_TAG ); + + FltReleaseContext( p2pCtx->VolCtx ); + + ExFreeToNPagedLookasideList( &Pre2PostContextList, + p2pCtx ); + + return FLT_POSTOP_FINISHED_PROCESSING; +} + + +VOID +ReadDriverParameters ( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine tries to read the driver-specific parameters from + the registry. These values will be found in the registry location + indicated by the RegistryPath passed in. + +Arguments: + + RegistryPath - the path key passed to the driver during driver entry. + +Return Value: + + None. + +--*/ +{ + OBJECT_ATTRIBUTES attributes; + HANDLE driverRegKey; + NTSTATUS status; + ULONG resultLength; + UNICODE_STRING valueName; + UCHAR buffer[sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + sizeof( LONG )]; + + // + // If this value is not zero then somebody has already explicitly set it + // so don't override those settings. + // + + if (0 == LoggingFlags) { + + // + // Open the desired registry key + // + + InitializeObjectAttributes( &attributes, + RegistryPath, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, + NULL ); + + status = ZwOpenKey( &driverRegKey, + KEY_READ, + &attributes ); + + if (!NT_SUCCESS( status )) { + + return; + } + + // + // Read the given value from the registry. + // + + RtlInitUnicodeString( &valueName, L"DebugFlags" ); + + status = ZwQueryValueKey( driverRegKey, + &valueName, + KeyValuePartialInformation, + buffer, + sizeof(buffer), + &resultLength ); + + if (NT_SUCCESS( status )) { + + LoggingFlags = *((PULONG) &(((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data)); + } + + // + // Close the registry entry + // + + ZwClose(driverRegKey); + } +} + diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.inf b/filesys/miniFilter/swapBuffers/swapBuffers.inf new file mode 100644 index 00000000..d6a5fc3a --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.inf @@ -0,0 +1,95 @@ +;;; +;;; SwapBuffers +;;; +;;; +;;; Copyright (c) 2001, Microsoft Corporation +;;; + +[Version] +signature = "$Windows NT$" +Class = "Encryption" ;This is determined by the work this filter driver does +ClassGuid = {a0a701c0-a511-42ff-aa6c-06dc0395576f} ;This value is determined by the Class +Provider = %Msft% +DriverVer = 06/16/2007,1.0.0.3 +CatalogFile = swapbuffers.cat + + +[DestinationDirs] +DefaultDestDir = 12 +MiniFilter.DriverFiles = 12 ;%windir%\system32\drivers + +;; +;; Default install sections +;; + +[DefaultInstall] +OptionDesc = %ServiceDescription% +CopyFiles = MiniFilter.DriverFiles + +[DefaultInstall.Services] +AddService = %ServiceName%,,MiniFilter.Service + +;; +;; Default uninstall sections +;; + +[DefaultUninstall] +DelFiles = MiniFilter.DriverFiles + +[DefaultUninstall.Services] +DelService = SwapBuffers,0x200 ;Ensure service is stopped before deleting + +; +; Services Section +; + +[MiniFilter.Service] +DisplayName = %ServiceName% +Description = %ServiceDescription% +ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ +Dependencies = "FltMgr" +ServiceType = 2 ;SERVICE_FILE_SYSTEM_DRIVER +;StartType = 0 ;SERVICE_BOOT_START +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +LoadOrderGroup = "FSFilter Encryption" +AddReg = MiniFilter.AddRegistry + +; +; Registry Modifications +; + +[MiniFilter.AddRegistry] +HKR,,"SupportedFeatures",0x00010001,0x3 +HKR,"Instances","DefaultInstance",0x00000000,%Instance1.Name% +HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% + +; +; Copy Files +; + +[MiniFilter.DriverFiles] +%DriverName%.sys + +[SourceDisksFiles] +swapbuffers.sys = 1,, + +[SourceDisksNames] +1 = %DiskId1%,,, + +;; +;; String Section +;; + +[Strings] +Msft = "Microsoft Corporation" +ServiceDescription = "Swap Buffers Sample Mini-Filter Driver" +ServiceName = "SwapBuffers" +DriverName = "SwapBuffers" +DiskId1 = "SwapBuffers Device Installation Disk" + +;Instances specific information. +Instance1.Name = "SwapBuffers Instance" +Instance1.Altitude = "141000" +Instance1.Flags = 0x0 ; allow automatic attachments diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.rc b/filesys/miniFilter/swapBuffers/swapBuffers.rc new file mode 100644 index 00000000..436c15cf --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.rc @@ -0,0 +1,10 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "SwapBuffers Filter Driver" +#define VER_INTERNALNAME_STR "swapBuffers.sys" + +#include "common.ver" diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.sln b/filesys/miniFilter/swapBuffers/swapBuffers.sln new file mode 100644 index 00000000..88ca2992 --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "swapBuffers", "swapBuffers.vcxproj", "{A7B7D32A-A301-4497-A68B-349341047B69}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|Win32.ActiveCfg = Debug|Win32 + {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|Win32.Build.0 = Debug|Win32 + {A7B7D32A-A301-4497-A68B-349341047B69}.Release|Win32.ActiveCfg = Release|Win32 + {A7B7D32A-A301-4497-A68B-349341047B69}.Release|Win32.Build.0 = Release|Win32 + {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|x64.ActiveCfg = Debug|x64 + {A7B7D32A-A301-4497-A68B-349341047B69}.Debug|x64.Build.0 = Debug|x64 + {A7B7D32A-A301-4497-A68B-349341047B69}.Release|x64.ActiveCfg = Release|x64 + {A7B7D32A-A301-4497-A68B-349341047B69}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj new file mode 100644 index 00000000..3c7b7151 --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{A7B7D32A-A301-4497-A68B-349341047B69}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{3AA28CE9-14ED-49C0-9FC7-A918130F15DA}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverType>WDM</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>swapBuffers</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>swapBuffers</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>swapBuffers</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>swapBuffers</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\fltMgr.lib</AdditionalDependencies> + </Link> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE;POOL_NX_OPTIN=1</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="swapBuffers.c" /> + <ResourceCompile Include="swapBuffers.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters new file mode 100644 index 00000000..eba4de22 --- /dev/null +++ b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{35572264-BD73-4FA0-88AC-7FCC7C0917C7}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{FF6920BD-0267-4DBD-B420-DED4DD132F5C}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{2BAFD988-E41F-4DB0-A580-19F92CFFE2F4}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{0D9FC8FE-1009-48A7-8A94-DD71E176AEE9}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="swapBuffers.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="swapBuffers.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file |
