summaryrefslogtreecommitdiff
path: root/filesys/miniFilter/delete/delete.c
diff options
context:
space:
mode:
Diffstat (limited to 'filesys/miniFilter/delete/delete.c')
-rw-r--r--filesys/miniFilter/delete/delete.c3253
1 files changed, 3253 insertions, 0 deletions
diff --git a/filesys/miniFilter/delete/delete.c b/filesys/miniFilter/delete/delete.c
new file mode 100644
index 00000000..ec515c81
--- /dev/null
+++ b/filesys/miniFilter/delete/delete.c
@@ -0,0 +1,3253 @@
+/*++
+
+Copyright (c) 1999 - 2002 Microsoft Corporation
+
+Module Name:
+
+ delete.c
+
+Abstract:
+
+ This is the main file for the delete detection sample minifilter.
+
+
+Environment:
+
+ Kernel mode
+
+
+--*/
+
+
+#include <fltKernel.h>
+#include <dontuse.h>
+#include <suppress.h>
+
+#pragma prefast(disable:__WARNING_ENCODE_MEMBER_FUNCTION_POINTER, "Not valid for kernel mode drivers")
+
+
+
+#define DFDBG_TRACE_ERRORS 0x00000001
+#define DFDBG_TRACE_ROUTINES 0x00000002
+#define DFDBG_TRACE_OPERATION_STATUS 0x00000004
+
+#define DF_VOLUME_GUID_NAME_SIZE 48
+
+#define DF_INSTANCE_CONTEXT_POOL_TAG 'nIfD'
+#define DF_STREAM_CONTEXT_POOL_TAG 'xSfD'
+#define DF_TRANSACTION_CONTEXT_POOL_TAG 'xTfD'
+#define DF_ERESOURCE_POOL_TAG 'sRfD'
+#define DF_DELETE_NOTIFY_POOL_TAG 'nDfD'
+#define DF_STRING_POOL_TAG 'rSfD'
+
+#define DF_CONTEXT_POOL_TYPE PagedPool
+
+#define DF_NOTIFICATION_MASK (TRANSACTION_NOTIFY_COMMIT_FINALIZE | \
+ TRANSACTION_NOTIFY_ROLLBACK)
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Macros //
+//////////////////////////////////////////////////////////////////////////////
+
+#define DF_PRINT( ... ) \
+ DbgPrintEx( DPFLTR_FLTMGR_ID, DPFLTR_ERROR_LEVEL, __VA_ARGS__ )
+
+#define DF_DBG_PRINT( _dbgLevel, ... ) \
+ (FlagOn( gTraceFlags, (_dbgLevel) ) ? \
+ DF_PRINT( __VA_ARGS__ ): \
+ (0))
+
+#define FlagOnAll( F, T ) \
+ (FlagOn( F, T ) == T)
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Main Globals //
+//////////////////////////////////////////////////////////////////////////////
+
+PFLT_FILTER gFilterHandle;
+ULONG gTraceFlags = DFDBG_TRACE_ERRORS;
+
+
+//////////////////////////////////////////////////////////////////////////////
+// ReFS Compatibility Helpers //
+//////////////////////////////////////////////////////////////////////////////
+
+//
+// This helps us deal with ReFS 128-bit file IDs and NTFS 64-bit file IDs.
+//
+
+typedef union _DF_FILE_REFERENCE {
+
+ struct {
+ ULONGLONG Value; // The 64-bit file ID lives here.
+ ULONGLONG UpperZeroes; // In a 64-bit file ID this will be 0.
+ } FileId64;
+
+ UCHAR FileId128[16]; // The 128-bit file ID lives here.
+
+} DF_FILE_REFERENCE, *PDF_FILE_REFERENCE;
+
+#define DfSizeofFileId(FID) ( \
+ ((FID).FileId64.UpperZeroes == 0ll) ? \
+ sizeof((FID).FileId64.Value) : \
+ sizeof((FID).FileId128) \
+ )
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Types //
+//////////////////////////////////////////////////////////////////////////////
+
+//
+// This is the instance context for this minifilter, it stores the volume's
+// GUID name.
+//
+
+typedef struct _DF_INSTANCE_CONTEXT {
+
+ //
+ // Volume GUID name.
+ //
+
+ UNICODE_STRING VolumeGuidName;
+
+} DF_INSTANCE_CONTEXT, *PDF_INSTANCE_CONTEXT;
+
+
+//
+// This is the stream context for this minifilter, attached whenever a stream
+// becomes a candidate for deletion.
+//
+
+typedef struct _DF_STREAM_CONTEXT {
+
+ //
+ // FLT_FILE_NAME_INFORMATION structure with the names for this stream
+ // and file. This is only used for printing out the opened name when
+ // notifying deletes. This will be the result of an opened query name
+ // done at the last pre-cleanup on the file/stream.
+ //
+ // Therefore, there is no requirement of maintaining the file name
+ // information (for the purposes we use it) in sync with the FltMgr name
+ // cache or the file system. This makes it okay to store it in the stream
+ // context.
+ //
+
+ PFLT_FILE_NAME_INFORMATION NameInfo;
+
+ //
+ // File ID, obtained from querying the file system for FileInternalInformation.
+ // If the File ID is 128 bits (as in ReFS) we get it via FileIdInformation.
+ //
+
+ DF_FILE_REFERENCE FileId;
+
+ //
+ // Number of SetDisp operations in flight.
+ //
+
+ volatile LONG NumOps;
+
+ //
+ // IsNotified == 1 means a file/stream deletion was already notified.
+ //
+
+ volatile LONG IsNotified;
+
+ //
+ // Whether or not we've already queried the file ID.
+ //
+
+ BOOLEAN FileIdSet;
+
+ //
+ // Delete Disposition for this stream.
+ //
+
+ BOOLEAN SetDisp;
+
+ //
+ // Delete-on-Close state for this stream.
+ //
+
+ BOOLEAN DeleteOnClose;
+
+} DF_STREAM_CONTEXT, *PDF_STREAM_CONTEXT;
+
+
+//
+// This is the transaction context for this minifilter, attached at post-
+// -cleanup when notifying a delete within a transaction.
+//
+
+typedef struct _DF_TRANSACTION_CONTEXT {
+
+ //
+ // List of DF_DELETE_NOTIFY structures representing pending delete
+ // notifications.
+ //
+
+ LIST_ENTRY DeleteNotifyList;
+
+ //
+ // ERESOURCE for synchronized access to the DeleteNotifyList.
+ //
+ // ERESOURCEs must be allocated from NonPagedPool. If an ERESOURCE was
+ // declared here as a direct member of a structure, instead of just a
+ // pointer, then the whole transaction context would need to be allocated
+ // out of NonPagedPool.
+ //
+ // Therefore, declaring it as a pointer and only allocating at context
+ // initialization time helps us save some NonPagedPool. This is
+ // particularly important in larger context structures.
+ //
+
+ PERESOURCE Resource;
+
+} DF_TRANSACTION_CONTEXT, *PDF_TRANSACTION_CONTEXT;
+
+
+//
+// This structure represents pending delete notifications for files that have
+// been deleted in an open transaction.
+//
+
+typedef struct _DF_DELETE_NOTIFY {
+
+ //
+ // Links to other DF_DELETE_NOTIFY structures in the list.
+ //
+
+ LIST_ENTRY Links;
+
+ //
+ // Pointer to the stream context for the deleted stream/file.
+ //
+
+ PDF_STREAM_CONTEXT StreamContext;
+
+ //
+ // TRUE for a deleted file, FALSE for a stream.
+ //
+
+ BOOLEAN FileDelete;
+
+} DF_DELETE_NOTIFY, *PDF_DELETE_NOTIFY;
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Prototypes //
+//////////////////////////////////////////////////////////////////////////////
+
+DRIVER_INITIALIZE DriverEntry;
+NTSTATUS
+DriverEntry (
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+ );
+
+NTSTATUS
+DfUnload (
+ _In_ FLT_FILTER_UNLOAD_FLAGS Flags
+ );
+
+NTSTATUS
+DfInstanceSetup (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
+ _In_ DEVICE_TYPE VolumeDeviceType,
+ _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
+ );
+
+NTSTATUS
+DfInstanceQueryTeardown (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
+ );
+
+VOID
+DfInstanceTeardownStart (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
+ );
+
+VOID
+DfInstanceTeardownComplete (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
+ );
+
+NTSTATUS
+DfSetupInstanceContext(
+ _In_ PCFLT_RELATED_OBJECTS FltObjects
+ );
+
+NTSTATUS
+DfAllocateContext (
+ _In_ FLT_CONTEXT_TYPE ContextType,
+ _Outptr_ PFLT_CONTEXT *Context
+ );
+
+NTSTATUS
+DfSetContext (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target,
+ _In_ FLT_CONTEXT_TYPE ContextType,
+ _In_ PFLT_CONTEXT NewContext,
+ _Outptr_opt_result_maybenull_ PFLT_CONTEXT *OldContext
+ );
+
+NTSTATUS
+DfGetContext (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target,
+ _In_ FLT_CONTEXT_TYPE ContextType,
+ _Outptr_ PFLT_CONTEXT *Context
+ );
+
+NTSTATUS
+DfGetOrSetContext (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target,
+ _Outptr_ _Pre_valid_ PFLT_CONTEXT *Context,
+ _In_ FLT_CONTEXT_TYPE ContextType
+ );
+
+VOID
+DfStreamContextCleanupCallback (
+ _In_ PDF_STREAM_CONTEXT StreamContext,
+ _In_ FLT_CONTEXT_TYPE ContextType
+ );
+
+VOID
+DfTransactionContextCleanupCallback (
+ _In_ PDF_TRANSACTION_CONTEXT TransactionContext,
+ _In_ FLT_CONTEXT_TYPE ContextType
+ );
+
+VOID
+DfInstanceContextCleanupCallback (
+ _In_ PDF_INSTANCE_CONTEXT InstanceContext,
+ _In_ FLT_CONTEXT_TYPE ContextType
+ );
+
+NTSTATUS
+DfGetFileNameInformation (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _Inout_ PDF_STREAM_CONTEXT StreamContext
+ );
+
+NTSTATUS
+DfAllocateUnicodeString (
+ _Inout_ PUNICODE_STRING String
+ );
+
+VOID
+DfFreeUnicodeString (
+ _Inout_ PUNICODE_STRING String
+ );
+
+NTSTATUS
+DfBuildFileIdString (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_STREAM_CONTEXT StreamContext,
+ _Out_ PUNICODE_STRING String
+ );
+
+NTSTATUS
+DfDetectDeleteByFileId (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_STREAM_CONTEXT StreamContext
+ );
+
+NTSTATUS
+DfIsFileDeleted (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_STREAM_CONTEXT StreamContext,
+ _In_ BOOLEAN IsTransaction
+ );
+
+NTSTATUS
+DfAddTransDeleteNotify (
+ _Inout_ PDF_STREAM_CONTEXT StreamContext,
+ _Inout_ PDF_TRANSACTION_CONTEXT TransactionContext,
+ _In_ BOOLEAN FileDelete
+ );
+
+VOID
+DfNotifyDelete (
+ _In_ PDF_STREAM_CONTEXT StreamContext,
+ _In_ BOOLEAN IsFile,
+ _Inout_opt_ PDF_TRANSACTION_CONTEXT TransactionContext
+ );
+
+VOID
+DfNotifyDeleteOnTransactionEnd (
+ _In_ PDF_DELETE_NOTIFY DeleteNotify,
+ _In_ BOOLEAN Commit
+ );
+
+NTSTATUS
+DfProcessDelete (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_STREAM_CONTEXT StreamContext
+ );
+
+FLT_PREOP_CALLBACK_STATUS
+DfPreCreateCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Outptr_result_maybenull_ PVOID *CompletionContext
+ );
+
+FLT_POSTOP_CALLBACK_STATUS
+DfPostCreateCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PVOID CompletionContext,
+ _In_ FLT_POST_OPERATION_FLAGS Flags
+ );
+
+FLT_PREOP_CALLBACK_STATUS
+DfPreSetInfoCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
+ );
+
+FLT_POSTOP_CALLBACK_STATUS
+DfPostSetInfoCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PVOID CompletionContext,
+ _In_ FLT_POST_OPERATION_FLAGS Flags
+ );
+
+FLT_PREOP_CALLBACK_STATUS
+DfPreCleanupCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
+ );
+
+FLT_POSTOP_CALLBACK_STATUS
+DfPostCleanupCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PVOID CompletionContext,
+ _In_ FLT_POST_OPERATION_FLAGS Flags
+ );
+
+NTSTATUS
+DfTransactionNotificationCallback (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_TRANSACTION_CONTEXT TransactionContext,
+ _In_ ULONG NotificationMask
+ );
+
+NTSTATUS
+DfGetVolumeGuidName (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Inout_ PUNICODE_STRING VolumeGuidName
+ );
+
+NTSTATUS
+DfGetFileId (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _Inout_ PDF_STREAM_CONTEXT StreamContext
+ );
+
+//////////////////////////////////////////////////////////////////////////////
+// Text section assignments for all routines //
+//////////////////////////////////////////////////////////////////////////////
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, DfUnload)
+#pragma alloc_text(PAGE, DfInstanceSetup)
+#pragma alloc_text(PAGE, DfInstanceQueryTeardown)
+#pragma alloc_text(PAGE, DfInstanceTeardownStart)
+#pragma alloc_text(PAGE, DfInstanceTeardownComplete)
+#pragma alloc_text(PAGE, DfSetupInstanceContext)
+#pragma alloc_text(PAGE, DfAllocateContext)
+#pragma alloc_text(PAGE, DfSetContext)
+#pragma alloc_text(PAGE, DfGetContext)
+#pragma alloc_text(PAGE, DfGetOrSetContext)
+#pragma alloc_text(PAGE, DfStreamContextCleanupCallback)
+#pragma alloc_text(PAGE, DfTransactionContextCleanupCallback)
+#pragma alloc_text(PAGE, DfInstanceContextCleanupCallback)
+#pragma alloc_text(PAGE, DfGetFileNameInformation)
+#pragma alloc_text(PAGE, DfAllocateUnicodeString)
+#pragma alloc_text(PAGE, DfFreeUnicodeString)
+#pragma alloc_text(PAGE, DfBuildFileIdString)
+#pragma alloc_text(PAGE, DfDetectDeleteByFileId)
+#pragma alloc_text(PAGE, DfIsFileDeleted)
+#pragma alloc_text(PAGE, DfAddTransDeleteNotify)
+#pragma alloc_text(PAGE, DfNotifyDelete)
+#pragma alloc_text(PAGE, DfNotifyDeleteOnTransactionEnd)
+#pragma alloc_text(PAGE, DfProcessDelete)
+#pragma alloc_text(PAGE, DfPreCreateCallback)
+#pragma alloc_text(PAGE, DfPostCreateCallback)
+#pragma alloc_text(PAGE, DfPreSetInfoCallback)
+#pragma alloc_text(PAGE, DfPostSetInfoCallback)
+#pragma alloc_text(PAGE, DfPreCleanupCallback)
+#pragma alloc_text(PAGE, DfPostCleanupCallback)
+#pragma alloc_text(PAGE, DfTransactionNotificationCallback)
+#pragma alloc_text(PAGE, DfGetVolumeGuidName)
+#pragma alloc_text(PAGE, DfGetFileId)
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Context Registration //
+//////////////////////////////////////////////////////////////////////////////
+
+CONST FLT_CONTEXT_REGISTRATION Contexts[] = {
+
+ { FLT_INSTANCE_CONTEXT,
+ 0,
+ DfInstanceContextCleanupCallback,
+ sizeof(DF_INSTANCE_CONTEXT),
+ DF_INSTANCE_CONTEXT_POOL_TAG,
+ NULL,
+ NULL,
+ NULL },
+
+ { FLT_STREAM_CONTEXT,
+ 0,
+ DfStreamContextCleanupCallback,
+ sizeof(DF_STREAM_CONTEXT),
+ DF_STREAM_CONTEXT_POOL_TAG,
+ NULL,
+ NULL,
+ NULL },
+
+ { FLT_TRANSACTION_CONTEXT,
+ 0,
+ DfTransactionContextCleanupCallback,
+ sizeof(DF_TRANSACTION_CONTEXT),
+ DF_TRANSACTION_CONTEXT_POOL_TAG,
+ NULL,
+ NULL,
+ NULL },
+
+ { FLT_CONTEXT_END }
+
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Operation Registration //
+//////////////////////////////////////////////////////////////////////////////
+
+CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
+
+ { IRP_MJ_CREATE,
+ 0,
+ DfPreCreateCallback,
+ DfPostCreateCallback },
+
+ { IRP_MJ_SET_INFORMATION,
+ FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO,
+ DfPreSetInfoCallback,
+ DfPostSetInfoCallback },
+
+ { IRP_MJ_CLEANUP,
+ 0,
+ DfPreCleanupCallback,
+ DfPostCleanupCallback },
+
+ { IRP_MJ_OPERATION_END }
+
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Filter Registration //
+//////////////////////////////////////////////////////////////////////////////
+
+CONST FLT_REGISTRATION FilterRegistration = {
+
+ sizeof( FLT_REGISTRATION ), // Size
+ FLT_REGISTRATION_VERSION, // Version
+ 0, // Flags
+
+ Contexts, // Context
+ Callbacks, // Operation callbacks
+
+ DfUnload, // MiniFilterUnload
+
+ DfInstanceSetup, // InstanceSetup
+ DfInstanceQueryTeardown, // InstanceQueryTeardown
+ DfInstanceTeardownStart, // InstanceTeardownStart
+ DfInstanceTeardownComplete, // InstanceTeardownComplete
+ NULL, // GenerateFileName
+ NULL, // NormalizeNameComponent
+ NULL, // NormalizeContextCleanup
+ DfTransactionNotificationCallback, // TransactionNotification
+ NULL // NormalizeNameComponentEx
+
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// MiniFilter initialization and unload routines //
+//////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+DriverEntry (
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath
+ )
+/*++
+
+Routine Description:
+
+ This is the initialization routine for this miniFilter driver. This
+ registers with FltMgr and initializes all global data structures.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by the system to
+ represent this driver.
+
+ RegistryPath - Unicode string identifying where the parameters for this
+ driver are located in the registry.
+
+Return Value:
+
+ Returns STATUS_SUCCESS.
+
+--*/
+{
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER( RegistryPath );
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DriverEntry: Entered\n" );
+
+ //
+ // Default to NonPagedPoolNx for non paged pool allocations where supported.
+ //
+
+ ExInitializeDriverRuntime( DrvRtPoolNxOptIn );
+
+ //
+ // Register with FltMgr to tell it our callback routines
+ //
+
+ status = FltRegisterFilter( DriverObject,
+ &FilterRegistration,
+ &gFilterHandle );
+
+ ASSERT( NT_SUCCESS( status ) );
+
+ if (NT_SUCCESS( status )) {
+
+ //
+ // Start filtering i/o
+ //
+
+ status = FltStartFiltering( gFilterHandle );
+
+ if (!NT_SUCCESS( status )) {
+
+ FltUnregisterFilter( gFilterHandle );
+ }
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DfUnload (
+ _In_ FLT_FILTER_UNLOAD_FLAGS Flags
+ )
+/*++
+
+Routine Description:
+
+ This is the unload routine for this miniFilter driver. This is called
+ when the minifilter is about to be unloaded.
+
+Arguments:
+
+ Flags - Indicating if this is a mandatory unload.
+
+Return Value:
+
+ Returns the final status of this operation.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( Flags );
+
+ PAGED_CODE();
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfUnload: Entered\n" );
+
+ FltUnregisterFilter( gFilterHandle );
+
+ return STATUS_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Filter Instance Callbacks (Setup/Teardown/QueryTeardown) //
+//////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+DfInstanceSetup (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
+ _In_ DEVICE_TYPE VolumeDeviceType,
+ _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
+ )
+/*++
+
+Routine Description:
+
+ This routine is called whenever a new instance is created on a volume. This
+ gives us a chance to decide if we need to attach to this volume or not.
+
+ New instances are only created and attached to a volume if it is a writable
+ NTFS or ReFS volume.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance and its associated volume.
+
+ Flags - Flags describing the reason for this attach request.
+
+ VolumeFilesystemType - A FLT_FSTYPE_* value indicating which file system type
+ the Filter Manager is offering to attach us to.
+
+Return Value:
+
+ STATUS_SUCCESS - attach
+ STATUS_FLT_DO_NOT_ATTACH - do not attach
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ BOOLEAN isWritable = FALSE;
+
+ UNREFERENCED_PARAMETER( Flags );
+ UNREFERENCED_PARAMETER( VolumeDeviceType );
+
+ PAGED_CODE();
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfInstanceSetup: Entered\n" );
+
+ status = FltIsVolumeWritable( FltObjects->Volume,
+ &isWritable );
+
+ if (!NT_SUCCESS( status )) {
+
+ return STATUS_FLT_DO_NOT_ATTACH;
+ }
+
+ //
+ // Attaching to read-only volumes is pointless as you should not be able
+ // to delete files on such a volume.
+ //
+
+ if (isWritable) {
+
+ switch (VolumeFilesystemType) {
+
+ case FLT_FSTYPE_NTFS:
+ case FLT_FSTYPE_REFS:
+
+ status = STATUS_SUCCESS;
+ break;
+
+ default:
+
+ return STATUS_FLT_DO_NOT_ATTACH;
+ }
+
+ } else {
+
+ return STATUS_FLT_DO_NOT_ATTACH;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DfInstanceQueryTeardown (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
+ )
+/*++
+
+Routine Description:
+
+ This is called when an instance is being manually deleted by a
+ call to FltDetachVolume or FilterDetach thereby giving us a
+ chance to fail that detach request.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance and its associated volume.
+
+ Flags - Indicating where this detach request came from.
+
+Return Value:
+
+ Returns the status of this operation.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( FltObjects );
+ UNREFERENCED_PARAMETER( Flags );
+
+ PAGED_CODE();
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfInstanceQueryTeardown: Entered\n" );
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+DfInstanceTeardownStart (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine is called at the start of instance teardown.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance and its associated volume.
+
+ Flags - Reason why this instance is been deleted.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( FltObjects );
+ UNREFERENCED_PARAMETER( Flags );
+
+ PAGED_CODE();
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfInstanceTeardownStart: Entered\n" );
+}
+
+
+VOID
+DfInstanceTeardownComplete (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine is called at the end of instance teardown.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance and its associated volume.
+
+ Flags - Reason why this instance is been deleted.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( FltObjects );
+ UNREFERENCED_PARAMETER( Flags );
+
+ PAGED_CODE();
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfInstanceTeardownComplete: Entered\n" );
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Context manipulation functions //
+//////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+DfAllocateContext (
+ _In_ FLT_CONTEXT_TYPE ContextType,
+ _Outptr_ PFLT_CONTEXT *Context
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates and initializes a context of given type.
+
+Arguments:
+
+ ContextType - Type of context to be allocated/initialized.
+
+ Context - Pointer to a context pointer.
+
+Return Value:
+
+ Returns a status forwarded from FltAllocateContext.
+
+--*/
+{
+ NTSTATUS status;
+ PDF_TRANSACTION_CONTEXT transactionContext;
+
+ PAGED_CODE();
+
+ switch (ContextType) {
+
+ case FLT_STREAM_CONTEXT:
+
+ status = FltAllocateContext( gFilterHandle,
+ FLT_STREAM_CONTEXT,
+ sizeof(DF_STREAM_CONTEXT),
+ DF_CONTEXT_POOL_TYPE,
+ Context );
+
+ if (NT_SUCCESS( status )) {
+ RtlZeroMemory( *Context, sizeof(DF_STREAM_CONTEXT) );
+ }
+
+ return status;
+
+ case FLT_TRANSACTION_CONTEXT:
+
+ status = FltAllocateContext( gFilterHandle,
+ FLT_TRANSACTION_CONTEXT,
+ sizeof(DF_TRANSACTION_CONTEXT),
+ DF_CONTEXT_POOL_TYPE,
+ Context );
+
+ if (NT_SUCCESS( status )) {
+ RtlZeroMemory( *Context, sizeof(DF_TRANSACTION_CONTEXT) );
+
+ transactionContext = *Context;
+
+ InitializeListHead( &transactionContext->DeleteNotifyList );
+
+ transactionContext->Resource = ExAllocatePoolWithTag( NonPagedPool,
+ sizeof(ERESOURCE),
+ DF_ERESOURCE_POOL_TAG );
+
+ if (NULL == transactionContext->Resource) {
+ FltReleaseContext( transactionContext );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ExInitializeResourceLite( transactionContext->Resource );
+ }
+
+ return status;
+
+ case FLT_INSTANCE_CONTEXT:
+
+ status = FltAllocateContext( gFilterHandle,
+ FLT_INSTANCE_CONTEXT,
+ sizeof(DF_INSTANCE_CONTEXT),
+ DF_CONTEXT_POOL_TYPE,
+ Context );
+
+ if (NT_SUCCESS( status )) {
+ RtlZeroMemory( *Context, sizeof(DF_INSTANCE_CONTEXT) );
+ }
+
+ return status;
+
+ default:
+
+ return STATUS_INVALID_PARAMETER;
+ }
+}
+
+
+NTSTATUS
+DfSetContext (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target,
+ _In_ FLT_CONTEXT_TYPE ContextType,
+ _In_ PFLT_CONTEXT NewContext,
+ _Outptr_opt_result_maybenull_ PFLT_CONTEXT *OldContext
+ )
+/*++
+
+Routine Description:
+
+ This routine sets the given context to the target.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance and its associated volume.
+
+ Target - Pointer to the target to which we want to attach the
+ context. It will actually be either a FILE_OBJECT or
+ a KTRANSACTION. For instance contexts, it's ignored, as
+ the target is the FLT_INSTANCE itself, obtained from
+ Data->Iopb->TargetInstance.
+
+ ContextType - Type of context to get/allocate/attach. Also used to
+ disambiguate the target/context type as this minifilter
+ only has one type of context per target.
+
+ NewContext - Pointer to the context the caller wants to attach.
+
+ OldContext - Returns the context already attached to the target, if
+ that is the case.
+
+Return Value:
+
+ Returns a status forwarded from FltSetXxxContext.
+
+--*/
+{
+ PAGED_CODE();
+
+ switch (ContextType) {
+
+ case FLT_STREAM_CONTEXT:
+
+ return FltSetStreamContext( FltObjects->Instance,
+ (PFILE_OBJECT)Target,
+ FLT_SET_CONTEXT_KEEP_IF_EXISTS,
+ NewContext,
+ OldContext );
+
+ case FLT_TRANSACTION_CONTEXT:
+
+ return FltSetTransactionContext( FltObjects->Instance,
+ (PKTRANSACTION)Target,
+ FLT_SET_CONTEXT_KEEP_IF_EXISTS,
+ NewContext,
+ OldContext );
+
+ case FLT_INSTANCE_CONTEXT:
+
+ return FltSetInstanceContext( FltObjects->Instance,
+ FLT_SET_CONTEXT_KEEP_IF_EXISTS,
+ NewContext,
+ OldContext );
+
+ default:
+
+ ASSERT( !"Unexpected context type!\n" );
+
+ return STATUS_INVALID_PARAMETER;
+ }
+}
+
+
+NTSTATUS
+DfGetContext (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target,
+ _In_ FLT_CONTEXT_TYPE ContextType,
+ _Outptr_ PFLT_CONTEXT *Context
+ )
+/*++
+
+Routine Description:
+
+ This routine gets the given context from the target.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance and its associated volume.
+
+ Target - Pointer to the target from which we want to obtain the
+ context. It will actually be either a FILE_OBJECT or
+ a KTRANSACTION. For instance contexts, it's ignored, as
+ the target is the FLT_INSTANCE itself, obtained from
+ Data->Iopb->TargetInstance.
+
+ ContextType - Type of context to get. Also used to disambiguate
+ the target/context type as this minifilter
+ only has one type of context per target.
+
+ Context - Pointer returning a pointer to the attached context.
+
+Return Value:
+
+ Returns a status forwarded from FltSetXxxContext.
+
+--*/
+{
+ PAGED_CODE();
+
+ switch (ContextType) {
+
+ case FLT_STREAM_CONTEXT:
+
+ return FltGetStreamContext( FltObjects->Instance,
+ (PFILE_OBJECT)Target,
+ Context );
+
+ case FLT_TRANSACTION_CONTEXT:
+
+ return FltGetTransactionContext( FltObjects->Instance,
+ (PKTRANSACTION)Target,
+ Context );
+
+ case FLT_INSTANCE_CONTEXT:
+
+ return FltGetInstanceContext( FltObjects->Instance,
+ Context );
+
+ default:
+
+ return STATUS_INVALID_PARAMETER;
+ }
+}
+
+
+NTSTATUS
+DfGetOrSetContext (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _When_(ContextType==FLT_INSTANCE_CONTEXT, _In_opt_) _When_(ContextType!=FLT_INSTANCE_CONTEXT, _In_) PVOID Target,
+ _Outptr_ _Pre_valid_ PFLT_CONTEXT *Context,
+ _In_ FLT_CONTEXT_TYPE ContextType
+ )
+/*++
+
+Routine Description:
+
+ This routine obtains a context of type ContextType that is attached to
+ Target.
+
+ If a context is already attached to Target, it will be returned in
+ *Context. If a context is already attached, but *Context points to
+ another context, *Context will be released.
+
+ If no context is attached, and *Context points to a previously allocated
+ context, *Context will be attached to the Target.
+
+ Finally, if no previously allocated context is passed to this routine
+ (*Context is a NULL pointer), a new Context is created and then attached
+ to Target.
+
+ In case of race conditions (or the presence of a previously allocated
+ context at *Context), the existing attached context is returned via
+ *Context.
+
+ In case of a transaction context, this function will also enlist in the
+ transaction.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance and its associated volume.
+
+ Target - Pointer to the target to which we want to attach the
+ context. It will actually be either a FILE_OBJECT or
+ a KTRANSACTION. It is NULL for an Instance context.
+
+ Context - Pointer to a pointer to a context. Used both for
+ returning an allocated/attached context or for receiving
+ a context to attach to the Target.
+
+ ContextType - Type of context to get/allocate/attach. Also used to
+ disambiguate the target/context type as this minifilter
+ only has one type of context per target.
+
+Return Value:
+
+ Returns a status forwarded from Flt(((Get|Set)Xxx)|Allocate)Context or
+ FltEnlistInTransaction.
+
+--*/
+{
+ NTSTATUS status;
+ PFLT_CONTEXT newContext;
+ PFLT_CONTEXT oldContext;
+
+ PAGED_CODE();
+
+ ASSERT( NULL != Context );
+
+ newContext = *Context;
+
+ //
+ // Is there already a context attached to the target?
+ //
+
+ status = DfGetContext( FltObjects,
+ Target,
+ ContextType,
+ &oldContext );
+
+ if (STATUS_NOT_FOUND == status) {
+
+ //
+ // There is no attached context. This means we have to either attach the
+ // one provided by the caller or allocate a new one and attach it.
+ //
+
+ if (NULL == newContext) {
+
+ //
+ // No provided context. Allocate one.
+ //
+
+ status = DfAllocateContext( ContextType, &newContext );
+
+ if (!NT_SUCCESS( status )) {
+
+ //
+ // We failed to allocate.
+ //
+
+ return status;
+ }
+ }
+
+ } else if (!NT_SUCCESS( status )) {
+
+ //
+ // We failed trying to get a context from the target.
+ //
+
+ return status;
+
+ } else {
+
+ //
+ // There is already a context attached to the target, so return
+ // that context.
+ //
+ // If a context was provided by the caller, release it if it's not
+ // the one attached to the target.
+ //
+
+ //
+ // The caller is not allowed to set the same context on the target
+ // twice.
+ //
+ ASSERT( newContext != oldContext );
+
+ if (NULL != newContext) {
+
+ FltReleaseContext( newContext );
+ }
+
+ *Context = oldContext;
+ return status;
+ }
+
+ //
+ // At this point we should have a context to set on the target (newContext).
+ //
+
+ status = DfSetContext( FltObjects,
+ Target,
+ ContextType,
+ newContext,
+ &oldContext );
+
+ if (!NT_SUCCESS( status )) {
+
+ //
+ // FltSetStreamContext failed so we must release the new context.
+ //
+
+ FltReleaseContext( newContext );
+
+ if (STATUS_FLT_CONTEXT_ALREADY_DEFINED == status) {
+
+ //
+ // We're racing with some other call which managed to set the
+ // context before us. We will return that context instead, which
+ // will be in oldContext.
+ //
+
+ *Context = oldContext;
+ return STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // Failed to set the context. Return NULL.
+ //
+
+ *Context = NULL;
+ return status;
+ }
+ }
+
+ //
+ // If this is setting a transaction context, we want to enlist in the
+ // transaction as well.
+ //
+
+ if (FLT_TRANSACTION_CONTEXT == ContextType) {
+
+ status = FltEnlistInTransaction( FltObjects->Instance,
+ (PKTRANSACTION)Target,
+ newContext,
+ DF_NOTIFICATION_MASK );
+
+ }
+
+ //
+ // Setting the context was successful so just return newContext.
+ //
+
+ *Context = newContext;
+ return status;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Context Cleanup Callbacks //
+//////////////////////////////////////////////////////////////////////////////
+
+VOID
+DfStreamContextCleanupCallback (
+ _In_ PDF_STREAM_CONTEXT StreamContext,
+ _In_ FLT_CONTEXT_TYPE ContextType
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up a stream context. The only cleanup necessary is
+ releasing the FLT_FILE_NAME_INFORMATION object of the NameInfo field.
+
+Arguments:
+
+ StreamContext - Pointer to DF_STREAM_CONTEXT to be cleaned up.
+
+ ContextType - Type of StreamContext. Must be FLT_STREAM_CONTEXT.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( ContextType );
+
+ PAGED_CODE();
+
+ ASSERT( ContextType == FLT_STREAM_CONTEXT );
+
+ //
+ // Release NameInfo if present.
+ //
+
+ if (StreamContext->NameInfo != NULL) {
+
+ FltReleaseFileNameInformation(StreamContext->NameInfo);
+ StreamContext->NameInfo = NULL;
+ }
+}
+
+
+VOID
+DfTransactionContextCleanupCallback (
+ _In_ PDF_TRANSACTION_CONTEXT TransactionContext,
+ _In_ FLT_CONTEXT_TYPE ContextType
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up a transaction context.
+ This operation consists basically of walking the DeleteNotifyList and
+ deleting all the deletion notifications pending on behalf of this
+ transaction.
+
+Arguments:
+
+ TransactionContext - Pointer to DF_TRANSACTION_CONTEXT to be cleaned up.
+
+ ContextType - Type of TransactionContext. Must be FLT_TRANSACTION_CONTEXT.
+
+--*/
+{
+ PDF_DELETE_NOTIFY deleteNotify = NULL;
+
+ UNREFERENCED_PARAMETER( ContextType );
+
+ PAGED_CODE();
+
+ ASSERT( ContextType == FLT_TRANSACTION_CONTEXT );
+
+ if (NULL != TransactionContext->Resource) {
+
+ FltAcquireResourceExclusive( TransactionContext->Resource );
+
+ while (!IsListEmpty( &TransactionContext->DeleteNotifyList )) {
+
+ //
+ // Remove every DF_DELETE_NOTIFY, releasing their corresponding
+ // FLT_FILE_NAME_INFORMATION objects and freeing pool used by
+ // them.
+ //
+
+ deleteNotify = CONTAINING_RECORD( RemoveHeadList( &TransactionContext->DeleteNotifyList ),
+ DF_DELETE_NOTIFY,
+ Links );
+
+ FltReleaseContext( deleteNotify->StreamContext );
+ ExFreePool( deleteNotify );
+
+ }
+
+ FltReleaseResource( TransactionContext->Resource );
+
+ //
+ // Delete and free the DeleteNotifyList synchronization resource.
+ //
+
+ ExDeleteResourceLite( TransactionContext->Resource );
+ ExFreePool( TransactionContext->Resource );
+ }
+}
+
+
+VOID
+DfInstanceContextCleanupCallback (
+ _In_ PDF_INSTANCE_CONTEXT InstanceContext,
+ _In_ FLT_CONTEXT_TYPE ContextType
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up an instance context, which consists on freeing
+ pool used by the volume GUID name string.
+
+Arguments:
+
+ InstanceContext - Pointer to DF_INSTANCE_CONTEXT to be cleaned up.
+
+ ContextType - Type of InstanceContext. Must be FLT_INSTANCE_CONTEXT.
+
+--*/
+{
+ UNREFERENCED_PARAMETER( ContextType );
+
+ PAGED_CODE();
+
+ ASSERT( ContextType == FLT_INSTANCE_CONTEXT );
+
+ DfFreeUnicodeString( &InstanceContext->VolumeGuidName );
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Miscellaneous String, File Name and File ID Functions //
+//////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+DfGetFileNameInformation (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _Inout_ PDF_STREAM_CONTEXT StreamContext
+ )
+/*++
+
+Routine Description:
+
+ This routine gets and parses the file name information, obtains the File
+ ID and saves them in the stream context.
+
+Arguments:
+
+ Data - Pointer to FLT_CALLBACK_DATA.
+
+ StreamContext - Pointer to stream context that will receive the file
+ information.
+
+Return Value:
+
+ Returns statuses forwarded from Flt(Get|Parse)FileNameInformation or
+ FltQueryInformationFile.
+
+--*/
+{
+ NTSTATUS status;
+ PFLT_FILE_NAME_INFORMATION oldNameInfo;
+ PFLT_FILE_NAME_INFORMATION newNameInfo;
+
+ PAGED_CODE();
+
+ //
+ // FltGetFileNameInformation - this is enough for a file name.
+ //
+
+ status = FltGetFileNameInformation( Data,
+ (FLT_FILE_NAME_OPENED |
+ FLT_FILE_NAME_QUERY_DEFAULT),
+ &newNameInfo );
+
+ if (!NT_SUCCESS( status )) {
+ return status;
+ }
+
+ //
+ // FltParseFileNameInformation - this fills in the other gaps, like the
+ // stream name, if present.
+ //
+
+ status = FltParseFileNameInformation( newNameInfo );
+
+ if (!NT_SUCCESS( status )) {
+ return status;
+ }
+
+ //
+ // Now that we have a good NameInfo, set it in the context, replacing
+ // the previous one.
+ //
+
+ oldNameInfo = InterlockedExchangePointer( &StreamContext->NameInfo,
+ newNameInfo );
+
+ if (NULL != oldNameInfo) {
+
+ FltReleaseFileNameInformation( oldNameInfo );
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DfGetFileId (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _Inout_ PDF_STREAM_CONTEXT StreamContext
+ )
+/*++
+
+Routine Description:
+
+ This routine obtains the File ID and saves it in the stream context.
+
+Arguments:
+
+ Data - Pointer to FLT_CALLBACK_DATA.
+
+ StreamContext - Pointer to stream context that will receive the file
+ ID.
+
+Return Value:
+
+ Returns statuses forwarded from FltQueryInformationFile, including
+ STATUS_FILE_DELETED.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ FILE_INTERNAL_INFORMATION fileInternalInformation;
+
+ PAGED_CODE();
+
+ //
+ // Only query the file system for the file ID for the first time.
+ // This is just an optimization. It doesn't need any real synchronization
+ // because file IDs don't change.
+ //
+
+ if (!StreamContext->FileIdSet) {
+
+ //
+ // Querying for FileInternalInformation gives you the file ID.
+ //
+
+ status = FltQueryInformationFile( Data->Iopb->TargetInstance,
+ Data->Iopb->TargetFileObject,
+ &fileInternalInformation,
+ sizeof(FILE_INTERNAL_INFORMATION),
+ FileInternalInformation,
+ NULL );
+
+ if (NT_SUCCESS( status )) {
+
+ //
+ // ReFS uses 128-bit file IDs. FileInternalInformation supports 64-
+ // bit file IDs. ReFS signals that a particular file ID can only
+ // be represented in 128 bits by returning FILE_INVALID_FILE_ID as
+ // the file ID. In that case we need to use FileIdInformation.
+ //
+
+ if (fileInternalInformation.IndexNumber.QuadPart == FILE_INVALID_FILE_ID) {
+
+ FILE_ID_INFORMATION fileIdInformation;
+
+ status = FltQueryInformationFile( Data->Iopb->TargetInstance,
+ Data->Iopb->TargetFileObject,
+ &fileIdInformation,
+ sizeof(FILE_ID_INFORMATION),
+ FileIdInformation,
+ NULL );
+
+ if (NT_SUCCESS( status )) {
+
+ //
+ // We don't use DfSizeofFileId() here because we are not
+ // measuring the size of a DF_FILE_REFERENCE. We know we have
+ // a 128-bit value.
+ //
+
+ RtlCopyMemory( &StreamContext->FileId,
+ &fileIdInformation.FileId,
+ sizeof(StreamContext->FileId) );
+
+ //
+ // Because there's (currently) no support for 128-bit values in
+ // the compiler we need to ensure the setting of the ID and our
+ // remembering that the file ID was set occur in the right order.
+ //
+
+ KeMemoryBarrier();
+
+ StreamContext->FileIdSet = TRUE;
+ }
+
+ } else {
+
+ StreamContext->FileId.FileId64.Value = fileInternalInformation.IndexNumber.QuadPart;
+ StreamContext->FileId.FileId64.UpperZeroes = 0ll;
+
+ //
+ // Because there's (currently) no support for 128-bit values in
+ // the compiler we need to ensure the setting of the ID and our
+ // remembering that the file ID was set occur in the right order.
+ //
+
+ KeMemoryBarrier();
+
+ StreamContext->FileIdSet = TRUE;
+ }
+ }
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DfAllocateUnicodeString (
+ _Inout_ PUNICODE_STRING String
+ )
+/*++
+
+Routine Description:
+
+ This helper routine simply allocates a buffer for a UNICODE_STRING and
+ initializes its Length to zero.
+
+ It uses whatever value is present in the MaximumLength field as the size
+ for the allocation.
+
+Arguments:
+
+ String - Pointer to UNICODE_STRING.
+
+Return Value:
+
+ STATUS_INSUFFICIENT_RESOURCES if it was not possible to allocate the
+ buffer from pool.
+
+ STATUS_SUCCESS otherwise.
+
+--*/
+{
+ PAGED_CODE();
+
+ ASSERT( NULL != String );
+ ASSERT( 0 != String->MaximumLength );
+
+ String->Length = 0;
+
+ String->Buffer = ExAllocatePoolWithTag( DF_CONTEXT_POOL_TYPE,
+ String->MaximumLength,
+ DF_STRING_POOL_TAG );
+
+ if (NULL == String->Buffer) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+DfFreeUnicodeString (
+ _Inout_ PUNICODE_STRING String
+ )
+/*++
+
+Routine Description:
+
+ This helper routine frees the buffer of a UNICODE_STRING and resets its
+ Length to zero.
+
+Arguments:
+
+ String - Pointer to UNICODE_STRING.
+
+--*/
+{
+ PAGED_CODE();
+
+ ASSERT( NULL != String );
+ ASSERT( 0 != String->MaximumLength );
+
+ String->Length = 0;
+
+ if ( NULL != String->Buffer ) {
+
+ String->MaximumLength = 0;
+ ExFreePool( String->Buffer );
+ String->Buffer = NULL;
+ }
+}
+
+
+NTSTATUS
+DfGetVolumeGuidName (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Inout_ PUNICODE_STRING VolumeGuidName
+ )
+/*++
+
+Routine Description:
+
+ This helper routine returns a volume GUID name (with an added trailing
+ backslash for convenience) in the VolumeGuidName string passed by the
+ caller.
+
+ The volume GUID name is cached in the instance context for the instance
+ attached to the volume, and this function will set up an instance context
+ with the cached name on it if there isn't one already attached to the
+ instance.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ VolumeGuidName - Pointer to UNICODE_STRING, returning the volume GUID name.
+
+Return Value:
+
+ Return statuses forwarded by DfAllocateUnicodeString or
+ FltGetVolumeGuidName. On error, caller needs to DfFreeUnicodeString on
+ VolumeGuidName.
+
+--*/
+{
+ NTSTATUS status;
+ PUNICODE_STRING sourceGuidName;
+ PDF_INSTANCE_CONTEXT instanceContext = NULL;
+
+ PAGED_CODE();
+
+ //
+ // Obtain an instance context. Target is NULL for instance context, as
+ // the FLT_INSTANCE can be obtained from the FltObjects.
+ //
+
+ status = DfGetOrSetContext( FltObjects,
+ NULL,
+ &instanceContext,
+ FLT_INSTANCE_CONTEXT );
+
+ if (NT_SUCCESS( status )) {
+
+ //
+ // sourceGuidName is the source from where we'll copy the volume
+ // GUID name. Hopefully the name is present in the instance context
+ // already (buffer is not NULL) so we'll try to use that.
+ //
+
+ sourceGuidName = &instanceContext->VolumeGuidName;
+
+ if (NULL == sourceGuidName->Buffer) {
+
+ //
+ // The volume GUID name is not cached in the instance context
+ // yet, so we will have to query the volume for it and put it
+ // in the instance context, so future queries can get it directly
+ // from the context.
+ //
+
+ UNICODE_STRING tempString;
+
+ //
+ // Add sizeof(WCHAR) so it's possible to add a trailing backslash here.
+ //
+
+ tempString.MaximumLength = DF_VOLUME_GUID_NAME_SIZE *
+ sizeof(WCHAR) +
+ sizeof(WCHAR);
+
+ status = DfAllocateUnicodeString( &tempString );
+
+ if (!NT_SUCCESS( status )) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!%s: DfAllocateUnicodeString returned 0x%08x!\n",
+ __FUNCTION__,
+ status );
+
+ return status;
+ }
+
+ // while there is no guid name, don't do the open by id deletion logic.
+ // (it's actually better to defer obtaining the volume GUID name up to
+ // the point when we actually need it, in the open by ID scenario.)
+ status = FltGetVolumeGuidName( FltObjects->Volume,
+ &tempString,
+ NULL );
+
+ if (!NT_SUCCESS( status )) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!%s: FltGetVolumeGuidName returned 0x%08x!\n",
+ __FUNCTION__,
+ status );
+
+ DfFreeUnicodeString( &tempString );
+
+ return status;
+ }
+
+ //
+ // Append trailing backslash.
+ //
+
+ RtlAppendUnicodeToString( &tempString, L"\\" );
+
+ //
+ // Now set the sourceGuidName to the tempString. It is okay to
+ // set Length and MaximumLength with no synchronization because
+ // those will always be the same value (size of a volume GUID
+ // name with an extra trailing backslash).
+ //
+
+ sourceGuidName->Length = tempString.Length;
+ sourceGuidName->MaximumLength = tempString.MaximumLength;
+
+ //
+ // Setting the buffer, however, requires some synchronization,
+ // because another thread might be attempting to do the same,
+ // and even though they're exactly the same string, they're
+ // different allocations (buffers) so if the other thread we're
+ // racing with manages to set the buffer before us, we need to
+ // free our temporary string buffer.
+ //
+
+ InterlockedCompareExchangePointer( &sourceGuidName->Buffer,
+ tempString.Buffer,
+ NULL );
+
+ if (sourceGuidName->Buffer != tempString.Buffer) {
+
+ //
+ // We didn't manage to set the buffer, so let's free the
+ // tempString buffer.
+ //
+
+ DfFreeUnicodeString( &tempString );
+ }
+ }
+
+ //
+ // sourceGuidName now contains the correct GUID name, so copy that
+ // to the caller string.
+ //
+
+ RtlCopyUnicodeString( VolumeGuidName, sourceGuidName );
+
+ //
+ // We're done with the instance context.
+ //
+
+ FltReleaseContext( instanceContext );
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DfBuildFileIdString (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_STREAM_CONTEXT StreamContext,
+ _Out_ PUNICODE_STRING String
+ )
+/*++
+
+Routine Description:
+
+ This helper routine builds a string used to open a file by its ID.
+
+ It will assume the file ID is properly loaded in the stream context
+ (StreamContext->FileId).
+
+Arguments:
+
+ Data - Pointer to FLT_CALLBACK_DATA.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ StreamContext - Pointer to the stream context.
+
+ String - Pointer to UNICODE_STRING (output).
+
+Return Value:
+
+ Return statuses forwarded by DfAllocateUnicodeString or
+ FltGetInstanceContext.
+
+--*/
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ ASSERT( NULL != String );
+
+ //
+ // We'll compose the string with:
+ // 1. The volume GUID name.
+ // 2. A backslash
+ // 3. The File ID.
+ //
+
+ //
+ // Make sure the file ID is loaded in the StreamContext. Note that if the
+ // file has been deleted DfGetFileId will return STATUS_FILE_DELETED.
+ // Since we're interested in detecting whether the file has been deleted
+ // that's fine; the open-by-ID will not actually take place. We have to
+ // ensure it is loaded before building the string length below since we
+ // may get either a 64-bit or 128-bit file ID back.
+ //
+
+ status = DfGetFileId( Data,
+ StreamContext );
+
+ if (!NT_SUCCESS( status )) {
+
+ return status;
+ }
+
+ //
+ // First add the lengths of 1, 2, 3 and allocate accordingly.
+ // Note that ReFS understands both 64- and 128-bit file IDs when opening
+ // by ID, so whichever size we get back from DfSizeofFileId will work.
+ //
+
+ String->MaximumLength = DF_VOLUME_GUID_NAME_SIZE * sizeof(WCHAR) +
+ sizeof(WCHAR) +
+ DfSizeofFileId( StreamContext->FileId );
+
+ status = DfAllocateUnicodeString( String );
+
+ if (!NT_SUCCESS( status )) {
+
+ return status;
+ }
+
+ //
+ // Now obtain the volume GUID name with a trailing backslash (1 + 2).
+ //
+
+ // obtain volume GUID name here and cache it in the InstanceContext.
+ status = DfGetVolumeGuidName( FltObjects,
+ String );
+
+ if (!NT_SUCCESS( status )) {
+
+ DfFreeUnicodeString( String );
+
+ return status;
+ }
+
+ //
+ // Now append the file ID to the end of the string.
+ //
+
+ RtlCopyMemory( Add2Ptr( String->Buffer, String->Length ),
+ &StreamContext->FileId,
+ DfSizeofFileId( StreamContext->FileId ));
+
+ String->Length += DfSizeofFileId( StreamContext->FileId );
+
+ ASSERT( String->Length == String->MaximumLength );
+
+ return status;
+}
+
+
+NTSTATUS
+DfDetectDeleteByFileId (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_STREAM_CONTEXT StreamContext
+ )
+/*++
+
+Routine Description:
+
+ This helper routine detects a deleted file by attempting to open it using
+ its file ID.
+
+ If the file is successfully opened this routine closes the file before returning.
+
+Arguments:
+
+ Data - Pointer to FLT_CALLBACK_DATA.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ StreamContext - Pointer to the stream context.
+
+Return Value:
+
+ STATUS_FILE_DELETED - Returned through DfBuildFileIdString if the file has
+ been deleted.
+
+ STATUS_INVALID_PARAMETER - Returned from FltCreateFileEx2 when opening by ID
+ a file that doesn't exist.
+
+ STATUS_DELETE_PENDING - The file has been set to be deleted when the last handle
+ goes away, but there are still open handles.
+
+ Also any other NTSTATUS returned from DfBuildFileIdString, FltCreateFileEx2,
+ or FltClose.
+
+--*/
+{
+ NTSTATUS status;
+ UNICODE_STRING fileIdString;
+ HANDLE handle;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatus;
+ IO_DRIVER_CREATE_CONTEXT driverCreateContext;
+
+ PAGED_CODE();
+
+ //
+ // First build the file ID string. Note that this may fail with STATUS_FILE_DELETED
+ // and short-circuit our open-by-ID. Since we're really trying to see if
+ // the file is deleted, that's perfectly okay.
+ //
+
+ status = DfBuildFileIdString( Data,
+ FltObjects,
+ StreamContext,
+ &fileIdString );
+
+ if (!NT_SUCCESS( status )) {
+
+ return status;
+ }
+
+ InitializeObjectAttributes( &objectAttributes,
+ &fileIdString,
+ OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL );
+
+ //
+ // It is important to initialize the IO_DRIVER_CREATE_CONTEXT structure's
+ // TxnParameters. We'll always want to do this open on behalf of a
+ // transaction because opening the file by ID is the method we use to
+ // detect if the whole file still exists when we're in a transaction.
+ //
+
+ IoInitializeDriverCreateContext( &driverCreateContext );
+ driverCreateContext.TxnParameters =
+ IoGetTransactionParameterBlock( Data->Iopb->TargetFileObject );
+
+ status = FltCreateFileEx2( gFilterHandle,
+ Data->Iopb->TargetInstance,
+ &handle,
+ NULL,
+ FILE_READ_ATTRIBUTES,
+ &objectAttributes,
+ &ioStatus,
+ (PLARGE_INTEGER) NULL,
+ 0L,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN,
+ FILE_OPEN_REPARSE_POINT | FILE_OPEN_BY_FILE_ID,
+ (PVOID) NULL,
+ 0L,
+ IO_IGNORE_SHARE_ACCESS_CHECK,
+ &driverCreateContext );
+
+ if (NT_SUCCESS( status )) {
+
+ status = FltClose( handle );
+ ASSERT( NT_SUCCESS( status ) );
+ }
+
+ DfFreeUnicodeString( &fileIdString );
+
+ return status;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Deletion Verification & Processing Functions //
+//////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+DfIsFileDeleted (
+ _In_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_STREAM_CONTEXT StreamContext,
+ _In_ BOOLEAN IsTransaction
+ )
+/*++
+
+Routine Description:
+
+ This routine returns whether a file was deleted. It is called from
+ DfProcessDelete after an alternate data stream is deleted. This needs to
+ be done for the case when the last outstanding handle to a delete-pending
+ file is a handle to a delete-pending alternate data stream. When that
+ handle is closed, the whole file goes away, and we want to report a whole
+ file deletion, not just an alternate data stream deletion.
+
+Arguments:
+
+ Data - Pointer to the filter callbackData that is passed to us.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ StreamContext - Pointer to the stream context.
+
+ IsTransaction - TRUE if in a transaction, FALSE otherwise.
+
+Return Value:
+
+ STATUS_FILE_DELETED - The whole file was deleted.
+ Successful status - The file still exists, this was probably just a named
+ data stream being deleted.
+ Anything else - Failure in finding out if the file was deleted.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ FILE_OBJECTID_BUFFER fileObjectIdBuf;
+
+ FLT_FILESYSTEM_TYPE fileSystemType;
+
+ PAGED_CODE();
+
+ //
+ // We need to know whether we're on ReFS or NTFS.
+ //
+
+ status = FltGetFileSystemType( FltObjects->Instance,
+ &fileSystemType );
+
+ if (status != STATUS_SUCCESS) {
+
+ return status;
+ }
+
+ //
+ // FSCTL_GET_OBJECT_ID does not return STATUS_FILE_DELETED if the
+ // file was deleted in a transaction, and this is why we need another
+ // method for detecting if the file is still present: opening by ID.
+ //
+ // If we're on ReFS we also need to open by file ID because ReFS does not
+ // support object IDs.
+ //
+
+ if (IsTransaction ||
+ (fileSystemType == FLT_FSTYPE_REFS)) {
+
+ status = DfDetectDeleteByFileId( Data,
+ FltObjects,
+ StreamContext );
+
+ switch (status) {
+
+ case STATUS_INVALID_PARAMETER:
+
+ //
+ // The file was deleted. In this case, trying to open it
+ // by ID returns STATUS_INVALID_PARAMETER.
+ //
+
+ return STATUS_FILE_DELETED;
+
+ case STATUS_DELETE_PENDING:
+
+ //
+ // In this case, the main file still exists, but is in
+ // a delete pending state, so we return STATUS_SUCCESS,
+ // signaling it still exists and wasn't deleted by this
+ // operation.
+ //
+
+ return STATUS_SUCCESS;
+
+ default:
+
+ return status;
+ }
+
+ } else {
+
+ //
+ // When not in a transaction, attempting to get the object ID of the
+ // file is a cheaper alternative compared to opening the file by ID.
+ //
+
+ status = FltFsControlFile( Data->Iopb->TargetInstance,
+ Data->Iopb->TargetFileObject,
+ FSCTL_GET_OBJECT_ID,
+ NULL,
+ 0,
+ &fileObjectIdBuf,
+ sizeof(FILE_OBJECTID_BUFFER),
+ NULL );
+
+ switch (status) {
+
+ case STATUS_OBJECTID_NOT_FOUND:
+
+ //
+ // Getting back STATUS_OBJECTID_NOT_FOUND means the file
+ // still exists, it just doesn't have an object ID.
+
+ return STATUS_SUCCESS;
+
+ default:
+
+ //
+ // Else we just get back STATUS_FILE_DELETED if the file
+ // doesn't exist anymore, or some error status, so no
+ // status conversion is necessary.
+ //
+
+ NOTHING;
+ }
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DfAddTransDeleteNotify (
+ _Inout_ PDF_STREAM_CONTEXT StreamContext,
+ _Inout_ PDF_TRANSACTION_CONTEXT TransactionContext,
+ _In_ BOOLEAN FileDelete
+ )
+/*++
+
+Routine Description:
+
+ This routine adds a pending deletion notification (DF_DELETE_NOTIFY)
+ object to the transaction context DeleteNotifyList. It is called from
+ DfNotifyDelete when a file or stream gets deleted in a transaction.
+
+Arguments:
+
+ StreamContext - Pointer to the stream context.
+
+ TransactionContext - Pointer to the transaction context.
+
+ FileDelete - TRUE if this is a FILE deletion, FALSE if it's a STREAM
+ deletion.
+
+Return Value:
+
+ STATUS_SUCCESS.
+
+--*/
+{
+ PDF_DELETE_NOTIFY deleteNotify;
+
+ PAGED_CODE();
+
+ ASSERT( NULL != TransactionContext->Resource );
+
+ ASSERT( NULL != StreamContext );
+
+ deleteNotify = ExAllocatePoolWithTag( DF_CONTEXT_POOL_TYPE,
+ sizeof(DF_DELETE_NOTIFY),
+ DF_DELETE_NOTIFY_POOL_TAG );
+
+ if (NULL == deleteNotify) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory( deleteNotify, sizeof(DF_DELETE_NOTIFY) );
+
+ FltReferenceContext( StreamContext );
+ deleteNotify->StreamContext = StreamContext;
+ deleteNotify->FileDelete = FileDelete;
+
+ FltAcquireResourceExclusive( TransactionContext->Resource );
+
+ InsertTailList( &TransactionContext->DeleteNotifyList,
+ &deleteNotify->Links );
+
+ FltReleaseResource( TransactionContext->Resource );
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+DfNotifyDelete (
+ _In_ PDF_STREAM_CONTEXT StreamContext,
+ _In_ BOOLEAN IsFile,
+ _Inout_opt_ PDF_TRANSACTION_CONTEXT TransactionContext
+ )
+/*++
+
+Routine Description:
+
+ This routine does the processing after it is verified, in the post-cleanup
+ callback, that a file or stream were deleted. It sorts out whether it's a
+ file or a stream delete, whether this is in a transacted context or not,
+ and issues the appropriate notifications.
+
+Arguments:
+
+ StreamContext - Pointer to the stream context of the deleted file/stream.
+
+ IsFile - TRUE if deleting a file, FALSE for an alternate data stream.
+
+ TransactionContext - The transaction context. Present if in a transaction,
+ NULL otherwise.
+
+--*/
+{
+ PAGED_CODE();
+
+ if (InterlockedIncrement( &StreamContext->IsNotified ) <= 1) {
+
+ if (IsFile) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!DfPostCleanupCallback: "
+ "A file \"%wZ\" (%p) has been",
+ &StreamContext->NameInfo->Name,
+ StreamContext );
+
+ } else {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!DfPostCleanupCallback: "
+ "An alternate data stream \"%wZ\" (%p) has been",
+ &StreamContext->NameInfo->Name,
+ StreamContext );
+ }
+
+ //
+ // Flag that a delete has been notified on this file/stream.
+ //
+
+ if (NULL == TransactionContext) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ " deleted!\n" );
+
+ } else {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ " deleted in a transaction!\n" );
+
+ DfAddTransDeleteNotify( StreamContext,
+ TransactionContext,
+ IsFile );
+ }
+ }
+}
+
+
+VOID
+DfNotifyDeleteOnTransactionEnd (
+ _In_ PDF_DELETE_NOTIFY DeleteNotify,
+ _In_ BOOLEAN Commit
+ )
+/*++
+
+Routine Description:
+
+ This routine is called by the transaction notification callback to issue
+ the proper notifications for a file that has been deleted in the context
+ of that transaction.
+ The file will be reported as finally deleted, if the transaction was
+ committed, or "saved" if the transaction was rolled back.
+
+Arguments:
+
+ DeleteNotify - Pointer to the DF_DELETE_NOTIFY object that contains the
+ data necessary for issuing this notification.
+
+ Commit - TRUE if the transaction was committed, FALSE if it was
+ rolled back.
+
+--*/
+{
+ PAGED_CODE();
+
+ if (DeleteNotify->FileDelete) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!DfTransactionNotificationCallback: "
+ "A file \"%wZ\" (%p) has been",
+ &DeleteNotify->StreamContext->NameInfo->Name,
+ DeleteNotify->StreamContext );
+
+ } else {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!DfTransactionNotificationCallback: "
+ "An alternate data stream \"%wZ\" (%p) has been",
+ &DeleteNotify->StreamContext->NameInfo->Name,
+ DeleteNotify->StreamContext );
+ }
+
+ if (Commit) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ " deleted due to a transaction commit!\n" );
+
+ } else {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ " saved due to a transaction rollback!\n" );
+ }
+}
+
+
+NTSTATUS
+DfProcessDelete (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_STREAM_CONTEXT StreamContext
+ )
+/*++
+
+Routine Description:
+
+ This routine does the processing after it is verified, in the post-cleanup
+ callback, that a file or stream were deleted. It sorts out whether it's a
+ file or a stream delete, whether this is in a transacted context or not,
+ and issues the appropriate notifications.
+
+Arguments:
+
+ Data - Pointer to the filter callbackData that is passed to us.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ StreamContext - Pointer to the stream context of the deleted file/stream.
+
+Return Value:
+
+ STATUS_SUCCESS.
+
+--*/
+{
+ BOOLEAN isTransaction;
+ BOOLEAN isFileDeleted = FALSE;
+ NTSTATUS status;
+ PDF_TRANSACTION_CONTEXT transactionContext = NULL;
+
+ PAGED_CODE();
+
+ // Is this in a transacted context?
+ isTransaction = (NULL != FltObjects->Transaction);
+
+ if (isTransaction) {
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!DfProcessDelete: In a transaction!\n" );
+
+ status = DfGetOrSetContext( FltObjects,
+ FltObjects->Transaction,
+ &transactionContext,
+ FLT_TRANSACTION_CONTEXT );
+
+ if (!NT_SUCCESS( status )) {
+
+ return status;
+ }
+ }
+
+ //
+ // Notify deletion. If this is an Alternate Data Stream being deleted,
+ // check if the whole file was deleted (by calling DfIsFileDeleted) as
+ // this could be the last handle to a delete-pending file.
+ //
+
+ status = DfIsFileDeleted( Data,
+ FltObjects,
+ StreamContext,
+ isTransaction );
+
+ if (STATUS_FILE_DELETED == status) {
+
+ isFileDeleted = TRUE;
+ status = STATUS_SUCCESS;
+
+ } else if (!NT_SUCCESS( status )) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!%s: DfIsFileDeleted returned 0x%08x!\n",
+ __FUNCTION__,
+ status );
+
+ goto _exit;
+ }
+
+ DfNotifyDelete( StreamContext,
+ isFileDeleted,
+ transactionContext );
+
+_exit:
+
+ if (NULL != transactionContext) {
+
+ FltReleaseContext( transactionContext );
+ }
+
+ return status;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// MiniFilter Operation Callback Routines //
+//////////////////////////////////////////////////////////////////////////////
+
+FLT_PREOP_CALLBACK_STATUS
+DfPreCreateCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Outptr_result_maybenull_ PVOID *CompletionContext
+ )
+/*++
+
+Routine Description:
+
+ This routine is the pre-operation completion routine for
+ IRP_MJ_CREATE in this miniFilter.
+
+ In the pre-create phase we're concerned with creates with
+ FILE_DELETE_ON_CLOSE set, and in those cases we want to flag
+ this stream as a candidate for being deleted.
+
+Arguments:
+
+ Data - Pointer to the filter callbackData that is passed to us.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ CompletionContext - The context for the completion routine for this
+ operation.
+
+Return Value:
+
+ FLT_PREOP_SUCCESS_WITH_CALLBACK - When FILE_DELETE_ON_CLOSE is set and
+ a stream context is created.
+
+ FLT_PREOP_SUCCESS_NO_CALLBACK - When FILE_DELETE_ON_CLOSE is not set
+ and no stream context is created.
+
+--*/
+{
+ PDF_STREAM_CONTEXT streamContext;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER( FltObjects );
+
+ PAGED_CODE();
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfPreCreateCallback: Entered\n" );
+
+ //
+ // Creates are only interesting in the FILE_DELETE_ON_CLOSE scenario,
+ // in which we'll want to flag this file as a candidate for being
+ // deleted.
+ //
+ // The way we do that is allocate a stream context for this and return
+ // FLT_PREOP_SUCCESS_NO_CALLBACK, passing down the stream context via
+ // the completion context, so that the post-create callback can, in case
+ // of a successful create, attach this context to the stream and flag it
+ // as a real deletion candidate.
+ //
+
+ if (FlagOn( Data->Iopb->Parameters.Create.Options, FILE_DELETE_ON_CLOSE )) {
+
+ status = DfAllocateContext( FLT_STREAM_CONTEXT,
+ &streamContext );
+
+ if (NT_SUCCESS( status )) {
+
+ *CompletionContext = (PVOID)streamContext;
+
+ return FLT_PREOP_SYNCHRONIZE;
+
+ } else {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!DfPreCreateCallback: An error occurred with DfAllocateStreamContext!\n" );
+ }
+ }
+
+ *CompletionContext = NULL;
+
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+}
+
+
+FLT_POSTOP_CALLBACK_STATUS
+DfPostCreateCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PVOID CompletionContext,
+ _In_ FLT_POST_OPERATION_FLAGS Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine is the post-operation completion routine for
+ IRP_MJ_CREATE in this miniFilter.
+
+ The post-create callback will only be called when this is a create with
+ FILE_DELETE_ON_CLOSE, meaning we have to flag it as a deletion candidate.
+
+Arguments:
+
+ Data - Pointer to the filter callbackData that is passed to us.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ CompletionContext - The context for the completion routine for this
+ operation. This will point to a DF_STREAM_CONTEXT allocated by
+ DfPreCreateCallback, which will be used for flagging this stream
+ as a deletion candidate.
+
+Return Value:
+
+ FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous
+ processing here.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PDF_STREAM_CONTEXT streamContext = NULL;
+
+ UNREFERENCED_PARAMETER( FltObjects );
+ UNREFERENCED_PARAMETER( Flags );
+
+ PAGED_CODE();
+
+ ASSERT( NULL != CompletionContext );
+
+ streamContext = (PDF_STREAM_CONTEXT)CompletionContext;
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfPostCreateCallback: Entered\n" );
+
+ // this status check handles the draining scenario.
+ if (NT_SUCCESS( Data->IoStatus.Status ) &&
+ (STATUS_REPARSE != Data->IoStatus.Status)) {
+
+ // assert we're not draining.
+ ASSERT( !FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) );
+
+ //
+ // Flag the stream as a deletion candidate: try setting the stream
+ // context on it to the stream context allocated by DfPreCreateCallback.
+ // If a context is already attached to the stream, DfGetOrSetContext
+ // will do the right thing and set streamContext to it, freeing the
+ // other context.
+ //
+
+ status = DfGetOrSetContext( FltObjects,
+ Data->Iopb->TargetFileObject,
+ &streamContext,
+ FLT_STREAM_CONTEXT );
+
+ if (NT_SUCCESS( status )) {
+
+ //
+ // Set DeleteOnClose on the stream context: a delete-on-close stream will
+ // always be checked for deletion on cleanup.
+ //
+
+ streamContext->DeleteOnClose = BooleanFlagOn( Data->Iopb->Parameters.Create.Options,
+ FILE_DELETE_ON_CLOSE );
+ }
+ }
+
+ //
+ // We will have a context in streamContext, because if allocation fails
+ // in DfPreCreateCallback, FLT_PREOP_SUCCESS_NO_CALLBACK is returned, so
+ // there is no post-create callback.
+ //
+ // If DfGetOrSetContext failed, if will have released streamContext
+ // already, so only release it if status is successful.
+ //
+
+ if (NT_SUCCESS( status )) {
+
+ FltReleaseContext( streamContext );
+ }
+
+ return FLT_POSTOP_FINISHED_PROCESSING;
+}
+
+
+FLT_PREOP_CALLBACK_STATUS
+DfPreSetInfoCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
+ )
+/*++
+
+Routine Description:
+
+ This routine is the pre-operation completion routine for
+ IRP_MJ_SET_INFORMATION in this miniFilter.
+
+ The pre-setinfo callback is important because setting
+ FileDispositionInformation is another way of putting the file in a
+ delete-pending state.
+
+ Since the delete disposition is a reversible condition, we have to
+ make sure to do the right thing when multiple operations are racing:
+ we won't be able to tell the the final outcome of the delete
+ disposition state of the stream, so everytime a race like that happens,
+ we assume this stream as a permanent deletion candidate, so it will be
+ checked for deletion in the post-cleanup callback.
+
+Arguments:
+
+ Data - Pointer to the filter callbackData that is passed to us.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ CompletionContext - The context for the completion routine for this
+ operation.
+
+Return Value:
+
+ FLT_PREOP_SYNCHRONIZE - we never do any sort of asynchronous processing
+ here, and we synchronize postop.
+
+ FLT_PREOP_SUCCESS_NO_CALLBACK - if not FileDispositionInformation or we
+ can't set a streamcontext.
+
+--*/
+{
+ NTSTATUS status;
+ PDF_STREAM_CONTEXT streamContext = NULL;
+ BOOLEAN race;
+
+ UNREFERENCED_PARAMETER( FltObjects );
+
+ PAGED_CODE();
+
+ switch (Data->Iopb->Parameters.SetFileInformation.FileInformationClass) {
+
+ case FileDispositionInformation:
+
+ //
+ // We're interested when the file delete disposition changes.
+ //
+
+ status = DfGetOrSetContext( FltObjects,
+ Data->Iopb->TargetFileObject,
+ &streamContext,
+ FLT_STREAM_CONTEXT );
+
+ if (!NT_SUCCESS( status )) {
+
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+ }
+
+ //
+ // Race detection logic. The NumOps field in the StreamContext
+ // counts the number of in-flight changes to delete disposition
+ // on the stream.
+ //
+ // If there's already some operations in flight, don't bother
+ // doing postop. Since there will be no postop, this value won't
+ // be decremented, staying forever 2 or more, which is one of
+ // the conditions for checking deletion at post-cleanup.
+ //
+
+ race = (InterlockedIncrement( &streamContext->NumOps ) > 1);
+
+ if (!race) {
+
+ //
+ // This is the only operation in flight, so do a postop on
+ // it because the final outcome of the delete disposition
+ // state of the stream is deterministic.
+ //
+
+ *CompletionContext = (PVOID)streamContext;
+
+ return FLT_PREOP_SYNCHRONIZE;
+
+ } else {
+
+ FltReleaseContext( streamContext );
+ }
+
+ // FALL_THROUGH
+
+ default:
+
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+
+ break;
+ }
+}
+
+
+FLT_POSTOP_CALLBACK_STATUS
+DfPostSetInfoCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PVOID CompletionContext,
+ _In_ FLT_POST_OPERATION_FLAGS Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine is the post-operation completion routine for
+ IRP_MJ_SET_INFORMATION in this miniFilter.
+
+ In this postop callback we will update the deletion disposition state
+ of this stream in the stream context. This callback will only be reached
+ when there's a single change to deletion disposition in flight for the
+ stream or when this was the first of many racing ops to hit the preop.
+
+ In the latter case, the race is already detected and adequately flagged
+ in the other preops, so we're safe just decrementing NumOps, because the
+ other operations will never reach postop and NumOps won't ever be
+ decremented for them, guaranteeing that NumOps will stay nonzero forever,
+ effectively flagging the race.
+
+Arguments:
+
+ Data - Pointer to the filter callbackData that is passed to us.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ CompletionContext - The context for the completion routine for this
+ operation.
+
+Return Value:
+
+ FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous
+ processing here.
+
+--*/
+{
+ PDF_STREAM_CONTEXT streamContext;
+
+ UNREFERENCED_PARAMETER( FltObjects );
+ UNREFERENCED_PARAMETER( Flags );
+
+ PAGED_CODE();
+
+ // assert on FileDispositionInformation
+ ASSERT( Data->Iopb->Parameters.SetFileInformation.FileInformationClass
+ == FileDispositionInformation );
+
+ // pass from pre-callback to post-callback
+ ASSERT( NULL != CompletionContext );
+ streamContext = (PDF_STREAM_CONTEXT) CompletionContext;
+
+ //
+ // Reaching a postop for FileDispositionInformation means we
+ // MUST have a stream context passed in the CompletionContext.
+ //
+
+ if (NT_SUCCESS( Data->IoStatus.Status )) {
+
+ //
+ // No synchronization is needed to set the SetDisp field,
+ // because in case of races, the NumOps field will be perpetually
+ // positive, and it being positive is already an indication this
+ // file is a delete candidate, so it will be checked at post-
+ // -cleanup regardless of the value of SetDisp.
+ //
+
+ streamContext->SetDisp = ((PFILE_DISPOSITION_INFORMATION)
+ Data->Iopb->Parameters.SetFileInformation.InfoBuffer)->DeleteFile;
+ }
+
+ //
+ // Now that the operation is over, decrement NumOps.
+ //
+
+ InterlockedDecrement( &streamContext->NumOps );
+
+ FltReleaseContext( streamContext );
+
+ return FLT_POSTOP_FINISHED_PROCESSING;
+}
+
+
+FLT_PREOP_CALLBACK_STATUS
+DfPreCleanupCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
+ )
+/*++
+
+Routine Description:
+
+ This routine is the pre-operation completion routine for
+ IRP_MJ_CLEANUP in this miniFilter.
+
+ In the preop callback for cleanup, we obtain the file information and
+ save it in the stream context, just so we have a name to use when
+ reporting file deletions.
+
+ That is done for every stream with an attached stream context because
+ those will be deletion candidates most of the time.
+
+Arguments:
+
+ Data - Pointer to the filter callbackData that is passed to us.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ CompletionContext - The context for the completion routine for this
+ operation.
+
+Return Value:
+
+ FLT_PREOP_SYNCHRONIZE - we never do any sort of asynchronous processing
+ here, and we want to synchronize the postop.
+
+ FLT_PREOP_SUCCESS_NO_CALLBACK - when we don't manage to get a stream
+ context.
+
+--*/
+{
+ PDF_STREAM_CONTEXT streamContext;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER( FltObjects );
+
+ PAGED_CODE();
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfPreCleanupCallback: Entered\n" );
+
+ status = FltGetStreamContext( Data->Iopb->TargetInstance,
+ Data->Iopb->TargetFileObject,
+ &streamContext );
+
+ if (NT_SUCCESS( status )) {
+
+ //
+ // Only streams with stream context will be sent for deletion check
+ // in post-cleanup, which makes sense because they would only ever
+ // have one if they were flagged as candidates at some point.
+ //
+ // Gather file information here so that we have a name to report.
+ // The name will be accurate most of the times, and in the cases it
+ // won't, it serves as a good clue and the stream context pointer
+ // value should offer a way to disambiguate that in case of renames
+ // etc.
+ //
+
+ status = DfGetFileNameInformation( Data, streamContext );
+
+ if (NT_SUCCESS( status )) {
+
+ // pass from pre-callback to post-callback
+ *CompletionContext = (PVOID)streamContext;
+
+ return FLT_PREOP_SYNCHRONIZE;
+
+ } else {
+
+ FltReleaseContext( streamContext );
+ }
+ }
+
+ return FLT_PREOP_SUCCESS_NO_CALLBACK;
+}
+
+
+FLT_POSTOP_CALLBACK_STATUS
+DfPostCleanupCallback (
+ _Inout_ PFLT_CALLBACK_DATA Data,
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PVOID CompletionContext,
+ _In_ FLT_POST_OPERATION_FLAGS Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine is the post-operation completion routine for
+ IRP_MJ_CLEANUP in this miniFilter.
+
+ Post-cleanup is the core of this minifilter. Here we check to see if
+ the stream or file were deleted and report that through DbgPrint.
+
+Arguments:
+
+ Data - Pointer to the filter callbackData that is passed to us.
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ CompletionContext - The completion context set in the pre-operation routine.
+
+ Flags - Denotes whether the completion is successful or is being drained.
+
+Return Value:
+
+ FLT_POSTOP_FINISHED_PROCESSING - we never do any sort of asynchronous
+ processing here.
+
+--*/
+{
+ FILE_STANDARD_INFORMATION fileInfo;
+ PDF_STREAM_CONTEXT streamContext = NULL;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER( CompletionContext );
+
+ UNREFERENCED_PARAMETER( Flags );
+
+ PAGED_CODE();
+
+ DF_DBG_PRINT( DFDBG_TRACE_ROUTINES,
+ "delete!DfPostCleanupCallback: Entered\n" );
+
+ // assert we're not draining.
+ ASSERT( !FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ) );
+
+ // pass from pre-callback to post-callback
+ ASSERT( NULL != CompletionContext );
+ streamContext = (PDF_STREAM_CONTEXT) CompletionContext;
+
+ if (NT_SUCCESS( Data->IoStatus.Status )) {
+
+ //
+ // Determine whether or not we should check for deletion. What
+ // flags a file as a deletion candidate is one or more of the following:
+ //
+ // 1. NumOps > 0. This means there are or were racing changes to
+ // the file delete disposition state, and, in that case,
+ // we don't know what that state is. So, let's err to the side of
+ // caution and check if it was deleted.
+ //
+ // 2. SetDisp. If this is TRUE and we haven't raced in setting delete
+ // disposition, this reflects the true delete disposition state of the
+ // file, meaning we must check for deletes if it is set to TRUE.
+ //
+ // 3. DeleteOnClose. If the file was ever opened with
+ // FILE_DELETE_ON_CLOSE, we must check to see if it was deleted.
+ //
+ // Also, if a deletion of this stream was already notified, there is no
+ // point notifying it again.
+ //
+
+ if (((streamContext->NumOps > 0) ||
+ (streamContext->SetDisp) ||
+ (streamContext->DeleteOnClose)) &&
+ (0 == streamContext->IsNotified)) {
+
+ //
+ // The check for deletion is done via a query to
+ // FileStandardInformation. If that returns STATUS_FILE_DELETED
+ // it means the stream was deleted.
+ //
+
+ status = FltQueryInformationFile( Data->Iopb->TargetInstance,
+ Data->Iopb->TargetFileObject,
+ &fileInfo,
+ sizeof(fileInfo),
+ FileStandardInformation,
+ NULL );
+
+ if (STATUS_FILE_DELETED == status) {
+
+ status = DfProcessDelete( Data,
+ FltObjects,
+ streamContext );
+
+ if (!NT_SUCCESS( status )) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!%s: It was not possible to verify "
+ "deletion due to an error in DfProcessDelete (0x%08x)!\n",
+ __FUNCTION__,
+ status );
+ }
+ }
+ }
+ }
+
+ FltReleaseContext( streamContext );
+
+ return FLT_POSTOP_FINISHED_PROCESSING;
+}
+
+
+NTSTATUS
+DfTransactionNotificationCallback (
+ _In_ PCFLT_RELATED_OBJECTS FltObjects,
+ _In_ PDF_TRANSACTION_CONTEXT TransactionContext,
+ _In_ ULONG NotificationMask
+ )
+/*++
+
+Routine Description:
+
+ This routine is the transaction notification callback for this minifilter.
+ It is called when a transaction we're enlisted in is committed or rolled
+ back so that it's possible to emit notifications about files that were
+ deleted in that transaction.
+
+Arguments:
+
+ FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
+ opaque handles to this filter, instance, its associated volume and
+ file object.
+
+ TransactionContext - The transaction context, set/modified when a delete
+ is detected.
+
+ NotificationMask - A mask of flags indicating the notifications received
+ from FltMgr. Should be either TRANSACTION_NOTIFY_COMMIT or
+ TRANSACTION_NOTIFY_ROLLBACK.
+
+Return Value:
+
+ STATUS_SUCCESS - This operation is never pended.
+
+--*/
+{
+ BOOLEAN commit = BooleanFlagOn( NotificationMask, TRANSACTION_NOTIFY_COMMIT_FINALIZE );
+ PDF_DELETE_NOTIFY deleteNotify = NULL;
+
+ UNREFERENCED_PARAMETER( FltObjects );
+
+ PAGED_CODE();
+
+ //
+ // There is no such thing as a simultaneous commit and rollback, nor
+ // should we get notifications for events other than a commit or a
+ // rollback.
+ //
+
+ ASSERT( (!FlagOnAll( NotificationMask, (DF_NOTIFICATION_MASK) )) &&
+ FlagOn( NotificationMask, (DF_NOTIFICATION_MASK) ) );
+
+ if (commit) {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!DfTransactionNotificationCallback: COMMIT!\n" );
+
+ } else {
+
+ DF_DBG_PRINT( DFDBG_TRACE_ERRORS,
+ "delete!DfTransactionNotificationCallback: ROLLBACK!\n" );
+ }
+
+ ASSERT( NULL != TransactionContext->Resource );
+
+ FltAcquireResourceExclusive( TransactionContext->Resource );
+
+ while (!IsListEmpty( &TransactionContext->DeleteNotifyList )) {
+
+ deleteNotify = CONTAINING_RECORD( RemoveHeadList( &TransactionContext->DeleteNotifyList ),
+ DF_DELETE_NOTIFY,
+ Links );
+
+ ASSERT( NULL != deleteNotify->StreamContext );
+
+ if (!commit) {
+ InterlockedDecrement( &deleteNotify->StreamContext->IsNotified );
+ }
+
+ DfNotifyDeleteOnTransactionEnd( deleteNotify,
+ commit );
+
+ // release stream context
+ FltReleaseContext( deleteNotify->StreamContext );
+ ExFreePool( deleteNotify );
+ }
+
+ FltReleaseResource( TransactionContext->Resource );
+
+ return STATUS_SUCCESS;
+}
+
+
+