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/MetadataManager | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'filesys/miniFilter/MetadataManager')
| -rw-r--r-- | filesys/miniFilter/MetadataManager/DataStore.c | 1082 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/MetadataManager.rc | 10 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/MetadataManager.sln | 28 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/MetadataManagerInit.c | 865 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/MetadataManagerProc.h | 255 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/MetadataManagerStruc.h | 197 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/ReadMe.md | 21 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/fmm.inf | 96 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/fmm.vcxproj | 183 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters | 40 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/operations.c | 1356 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/pch.h | 47 | ||||
| -rw-r--r-- | filesys/miniFilter/MetadataManager/support.c | 244 |
13 files changed, 4424 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; +} + |
