WDK Mini Filter Example
cancelSafe.c
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) 1999 - 2002 Microsoft Corporation
4 
5 Module Name:
6 
7  cancelSafe.c
8 
9 Abstract:
10 
11  This is the main module of the cancelSafe miniFilter driver.
12 
13 Environment:
14 
15  Kernel mode
16 
17 --*/
18 
19 #include <fltKernel.h>
20 #include <dontuse.h>
21 #include <suppress.h>
22 
23 
24 //
25 // Debug flags and helper functions
26 //
27 
28 #define CSQ_TRACE_ERROR 0x00000001
29 #define CSQ_TRACE_LOAD_UNLOAD 0x00000002
30 #define CSQ_TRACE_INSTANCE_CALLBACK 0x00000004
31 #define CSQ_TRACE_CONTEXT_CALLBACK 0x00000008
32 #define CSQ_TRACE_CBDQ_CALLBACK 0x00000010
33 #define CSQ_TRACE_PRE_READ 0x00000020
34 #define CSQ_TRACE_ALL 0xFFFFFFFF
35 
36 #define DebugTrace(Level, Data) \
37  if ((Level) & Globals.DebugLevel) { \
38  DbgPrint Data; \
39  }
40 
41 //
42 // Memory Pool Tags
43 //
44 
45 #define INSTANCE_CONTEXT_TAG 'IqsC'
46 #define QUEUE_CONTEXT_TAG 'QqsC'
47 #define CSQ_REG_TAG 'RqsC'
48 #define CSQ_STRING_TAG 'SqsC'
49 
50 //
51 // Registry value names and default values
52 //
53 
54 #define CSQ_DEFAULT_TIME_DELAY 150000000
55 #define CSQ_DEFAULT_MAPPING_PATH L"\\"
56 #define CSQ_KEY_NAME_DELAY L"OperatingDelay"
57 #define CSQ_KEY_NAME_PATH L"OperatingPath"
58 #define CSQ_KEY_NAME_DEBUG_LEVEL L"DebugLevel"
59 #define CSQ_MAX_PATH_LENGTH 256
60 
61 
62 //
63 // Prototypes
64 //
65 
66 //
67 // Queue context data structure
68 //
69 
70 typedef struct _QUEUE_CONTEXT {
71 
72  FLT_CALLBACK_DATA_QUEUE_IO_CONTEXT CbdqIoContext;
73 
75 
76 //
77 // Instance context data structure
78 //
79 
80 typedef struct _INSTANCE_CONTEXT {
81 
82  //
83  // Instance for this context.
84  //
85 
86  PFLT_INSTANCE Instance;
87 
88  //
89  // Cancel safe queue members
90  //
91 
92  FLT_CALLBACK_DATA_QUEUE Cbdq;
93  LIST_ENTRY QueueHead;
94  FAST_MUTEX Lock;
95 
96  //
97  // Flag to control the life/death of the work item thread
98  //
99 
100  volatile LONG WorkerThreadFlag;
101 
102  //
103  // Notify the worker thread that the instance is being torndown
104  //
105 
107 
109 
110 
111 typedef struct _CSQ_GLOBAL_DATA {
112 
113  ULONG DebugLevel;
114 
115  PFLT_FILTER FilterHandle;
116 
117  NPAGED_LOOKASIDE_LIST QueueContextLookaside;
118 
119  UNICODE_STRING MappingPath;
120 
121  PWSTR PathBuffer;
122 
123  LONGLONG TimeDelay;
124 
126 
127 
128 
129 //
130 // Global variables
131 //
132 
134 
135 
136 //
137 // Local function prototypes
138 //
139 
140 DRIVER_INITIALIZE DriverEntry;
141 NTSTATUS
142 DriverEntry (
143  _In_ PDRIVER_OBJECT DriverObject,
144  _In_ PUNICODE_STRING RegistryPath
145  );
146 
147 VOID
149  );
150 
151 NTSTATUS
152 Unload (
153  _In_ FLT_FILTER_UNLOAD_FLAGS Flags
154  );
155 
156 VOID
158  _In_ PFLT_CONTEXT Context,
159  _In_ FLT_CONTEXT_TYPE ContextType
160  );
161 
162 NTSTATUS
164  _In_ PCFLT_RELATED_OBJECTS FltObjects,
165  _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
166  _In_ DEVICE_TYPE VolumeDeviceType,
167  _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
168  );
169 
170 NTSTATUS
172  _In_ PCFLT_RELATED_OBJECTS FltObjects,
173  _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
174  );
175 
176 VOID
178  _In_ PCFLT_RELATED_OBJECTS FltObjects,
179  _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
180  );
181 
182 VOID
184  _In_ PCFLT_RELATED_OBJECTS FltObjects,
185  _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
186  );
187 
188 NTSTATUS
190  _In_ PUNICODE_STRING RegistryPath
191  );
192 
193 VOID
195 _IRQL_raises_(APC_LEVEL)
196 _Requires_lock_not_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
197 _Acquires_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
198 CsqAcquire(
199  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
200  _Out_ PKIRQL Irql
201  );
202 
203 VOID
204 _IRQL_requires_max_(APC_LEVEL)
205 _IRQL_requires_min_(APC_LEVEL)
206 _IRQL_raises_(PASSIVE_LEVEL)
207 _Requires_lock_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
208 _Releases_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
209 CsqRelease(
210  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
211  _In_ KIRQL Irql
212  );
213 
214 NTSTATUS
216  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
217  _In_ PFLT_CALLBACK_DATA Data,
218  _In_opt_ PVOID Context
219  );
220 VOID
222  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
223  _In_ PFLT_CALLBACK_DATA Data
224  );
225 PFLT_CALLBACK_DATA
227  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
228  _In_opt_ PFLT_CALLBACK_DATA Data,
229  _In_opt_ PVOID PeekContext
230  );
231 VOID
233  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
234  _Inout_ PFLT_CALLBACK_DATA Data
235  );
236 
237 FLT_PREOP_CALLBACK_STATUS
238 PreRead (
239  _Inout_ PFLT_CALLBACK_DATA Data,
240  _In_ PCFLT_RELATED_OBJECTS FltObjects,
241  _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
242  );
243 
244 VOID
246  _In_ PFLT_GENERIC_WORKITEM WorkItem,
247  _In_ PFLT_FILTER Filter,
248  _In_ PVOID Context
249  );
250 
251 NTSTATUS
253  _In_ PINSTANCE_CONTEXT InstanceContext
254  );
255 
256 NTSTATUS
258  _Inout_ PFLT_CALLBACK_DATA Data
259  );
260 
261 VOID
263  _In_ PINSTANCE_CONTEXT InstanceContext
264  );
265 
266 //
267 // Assign text sections for each routine.
268 //
269 
270 #ifdef ALLOC_PRAGMA
271 #pragma alloc_text(INIT, DriverEntry)
272 #pragma alloc_text(INIT, SetConfiguration)
273 #pragma alloc_text(PAGE, Unload)
274 #pragma alloc_text(PAGE, FreeGlobals)
275 #pragma alloc_text(PAGE, ContextCleanup)
276 #pragma alloc_text(PAGE, InstanceSetup)
277 #pragma alloc_text(PAGE, InstanceQueryTeardown)
278 #pragma alloc_text(PAGE, InstanceTeardownStart)
279 #pragma alloc_text(PAGE, InstanceTeardownComplete)
280 
281 #endif
282 
283 //
284 // Filters callback routines
285 //
286 
287 FLT_OPERATION_REGISTRATION Callbacks[] = {
288  { IRP_MJ_READ,
289  FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO,
290  PreRead,
291  NULL },
292 
293  { IRP_MJ_OPERATION_END }
294 };
295 
296 //
297 // Filters context registration data structure
298 //
299 
300 const FLT_CONTEXT_REGISTRATION ContextRegistration[] = {
301 
302  { FLT_INSTANCE_CONTEXT,
303  0,
305  sizeof( INSTANCE_CONTEXT ),
307 
308  { FLT_CONTEXT_END }
309 };
310 
311 //
312 // Filters registration data structure
313 //
314 
315 FLT_REGISTRATION FilterRegistration = {
316 
317  sizeof( FLT_REGISTRATION ), // Size
318  FLT_REGISTRATION_VERSION, // Version
319  0, // Flags
320  ContextRegistration, // Context
321  Callbacks, // Operation callbacks
322  Unload, // Filters unload routine
323  InstanceSetup, // InstanceSetup routine
324  InstanceQueryTeardown, // InstanceQueryTeardown routine
325  InstanceTeardownStart, // InstanceTeardownStart routine
326  InstanceTeardownComplete, // InstanceTeardownComplete routine
327  NULL, NULL, NULL // Unused naming support callbacks
328 };
329 
330 //
331 // Filter driver initialization and unload routines
332 //
333 
334 NTSTATUS
336  _In_ PDRIVER_OBJECT DriverObject,
337  _In_ PUNICODE_STRING RegistryPath
338  )
339 /*++
340 
341 Routine Description:
342 
343  This is the initialization routine for this filter driver. It registers
344  itself with the filter manager and initializes all its global data structures.
345 
346 Arguments:
347 
348  DriverObject - Pointer to driver object created by the system to
349  represent this driver.
350 
351  RegistryPath - Unicode string identifying where the parameters for this
352  driver are located in the registry.
353 
354 Return Value:
355 
356  Returns STATUS_SUCCESS.
357 
358 --*/
359 {
360  NTSTATUS Status;
361 
362  //
363  // Default to NonPagedPoolNx for non paged pool allocations where supported.
364  //
365 
366  ExInitializeDriverRuntime( DrvRtPoolNxOptIn );
367 
368  //
369  // Initialize global lookaside list
370  //
371 
372  ExInitializeNPagedLookasideList( &Globals.QueueContextLookaside,
373  NULL,
374  NULL,
375  0,
376  sizeof( QUEUE_CONTEXT ),
378  0 );
379 
380  //
381  // Initialize the configuration to default values
382  //
383 
384  Globals.DebugLevel = CSQ_TRACE_ERROR;
385 
387 
388  Globals.PathBuffer = NULL;
389 
390  RtlInitUnicodeString( &Globals.MappingPath, CSQ_DEFAULT_MAPPING_PATH );
391 
392 
393  //
394  // Modify the configuration based on values in the registry
395  //
396 
397  Status = SetConfiguration( RegistryPath );
398 
399  if (!NT_SUCCESS( Status )) {
400 
401  goto DriverEntryCleanup;
402  }
403 
405  ("[Csq]: CancelSafe!DriverEntry\n") );
406 
407 
408 
409  //
410  // Register with the filter manager
411  //
412 
413  Status = FltRegisterFilter( DriverObject,
414  &FilterRegistration,
415  &Globals.FilterHandle );
416 
417  if (!NT_SUCCESS( Status )) {
418 
420  ("[Csq]: Failed to register filter (Status = 0x%x)\n",
421  Status) );
422 
423  goto DriverEntryCleanup;
424 
425  }
426 
427  //
428  // Start filtering I/O
429  //
430 
431  Status = FltStartFiltering( Globals.FilterHandle );
432 
433  if (!NT_SUCCESS( Status )) {
434 
436  ("[Csq]: Failed to start filtering (Status = 0x%x)\n",
437  Status) );
438 
439  FltUnregisterFilter( Globals.FilterHandle );
440 
441  goto DriverEntryCleanup;
442 
443  }
444 
445 
447  ("[Csq]: Driver loaded complete\n") );
448 
449 DriverEntryCleanup:
450 
451  if (!NT_SUCCESS( Status )) {
452 
453  FreeGlobals();
454  }
455 
456  return Status;
457 }
458 
459 
460 NTSTATUS
462  _In_ PUNICODE_STRING RegistryPath
463  )
464 /*++
465 
466 Routine Description:
467 
468  This routine tries to configure the debuglevel, mapping path and
469  queue delay based on values in the registry.
470 
471 Arguments:
472 
473  RegistryPath - The path key passed to the driver during DriverEntry.
474 
475 Return Value:
476 
477  STATUS_SUCCESS if the function completes successfully. Otherwise a valid
478  NTSTATUS code is returned.
479 
480 --*/
481 {
482  NTSTATUS Status;
483  OBJECT_ATTRIBUTES Attributes;
484  HANDLE DriverRegKey = NULL;
485  UNICODE_STRING ValueName;
486  BOOLEAN CloseHandle = FALSE;
487  UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + CSQ_MAX_PATH_LENGTH * sizeof(WCHAR)];
488  PKEY_VALUE_PARTIAL_INFORMATION Value = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
489  ULONG ValueLength = sizeof(Buffer);
490  ULONG ResultLength;
491  ULONG Length;
492 
493  //
494  // Open the driver registry key.
495  //
496 
497  InitializeObjectAttributes( &Attributes,
498  RegistryPath,
499  OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
500  NULL,
501  NULL );
502 
503  Status = ZwOpenKey( &DriverRegKey,
504  KEY_READ,
505  &Attributes );
506 
507  if (!NT_SUCCESS( Status )) {
508 
509  goto SetConfigurationCleanup;
510  }
511 
512  CloseHandle = TRUE;
513 
514  //
515  // Query the debug level
516  //
517 
518  RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_DEBUG_LEVEL );
519 
520  Status = ZwQueryValueKey( DriverRegKey,
521  &ValueName,
523  Value,
524  ValueLength,
525  &ResultLength );
526 
527  if (NT_SUCCESS( Status )) {
528 
529  Globals.DebugLevel = *(PULONG)(Value->Data);
530  }
531 
532 
533  //
534  // Query the queue time delay
535  //
536 
537 
538  RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_DELAY );
539 
540  Status = ZwQueryValueKey( DriverRegKey,
541  &ValueName,
543  Value,
544  ValueLength,
545  &ResultLength );
546 
547  if (NT_SUCCESS( Status )) {
548 
549  if (Value->Type != REG_DWORD) {
550 
551  Status = STATUS_INVALID_PARAMETER;
552  goto SetConfigurationCleanup;
553  }
554 
555  Globals.TimeDelay = (LONGLONG)(*(PULONG)(Value->Data));
556 
557  }
558 
559  //
560  // Query the mapping path
561  //
562 
563  RtlInitUnicodeString( &ValueName, CSQ_KEY_NAME_PATH );
564 
565  //
566  // For simplicity of this sample, the length of the mapping path
567  // allowed in the registry is limited to CSQ_MAX_PATH_LENGTH
568  // characters. If this size is exceeded the default mapping path
569  // will be used.
570  //
571 
572  Status = ZwQueryValueKey( DriverRegKey,
573  &ValueName,
575  Value,
576  ValueLength,
577  &ValueLength );
578 
579  if (NT_SUCCESS( Status )) {
580 
581  //
582  // Set up the mapping and ensure the mapping string format is "\a\...\".
583  // If the mapping path doesn't begin with '\' fail, if it doesn't end
584  // with a '\' append one.
585  //
586 
587  if (*(PWCHAR)(Value->Data) != L'\\') {
588 
589  Status = STATUS_INVALID_PARAMETER;
590  goto SetConfigurationCleanup;
591  }
592 
593  //
594  // Allocate enough space for an extra character in case a trailing '\'
595  // is missing and needs to be added.
596  //
597 
598  Length = Value->DataLength + sizeof(WCHAR),
599 
600  Globals.PathBuffer = ExAllocatePoolWithTag( NonPagedPool, Length, CSQ_STRING_TAG );
601 
602  if (Globals.PathBuffer == NULL) {
603 
604  Status = STATUS_INSUFFICIENT_RESOURCES;
605  goto SetConfigurationCleanup;
606  }
607 
608  RtlCopyMemory( Globals.PathBuffer, Value->Data, Value->DataLength );
609 
610  Globals.PathBuffer[Length / sizeof(WCHAR) - 1] = L'\0';
611 
612  //
613  // Add a trailing '\' if one is missing.
614  //
615 
616  if (Globals.PathBuffer[Length/sizeof(WCHAR) - 3] != L'\\') {
617 
618  Globals.PathBuffer[Length/sizeof(WCHAR) - 2] = L'\\';
619 
620  }
621 
622  RtlInitUnicodeString(&Globals.MappingPath, Globals.PathBuffer);
623 
624  }
625 
626  //
627  // Ignore errors when looking for values in the registry.
628  // Default values will be used.
629  //
630 
631  Status = STATUS_SUCCESS;
632 
633 SetConfigurationCleanup:
634 
635  if (CloseHandle) {
636 
637  ZwClose( DriverRegKey );
638  }
639 
640  return Status;
641 
642 }
643 
644 
645 VOID
647  )
648 /*++
649 
650 Routine Descrition:
651 
652  This routine cleans up the global buffers on both
653  teardown and initialization failure.
654 
655 Arguments:
656 
657 Return Value:
658 
659  None.
660 
661 --*/
662 {
663  PAGED_CODE();
664 
665  Globals.FilterHandle = NULL;
666 
667  ExDeleteNPagedLookasideList( &Globals.QueueContextLookaside );
668 
669  if (Globals.PathBuffer != NULL) {
670 
671  ExFreePoolWithTag( Globals.PathBuffer, CSQ_STRING_TAG );
672  Globals.PathBuffer = NULL;
673  }
674 
675  RtlInitUnicodeString( &Globals.MappingPath, NULL );
676 }
677 
678 
679 NTSTATUS
681  _In_ FLT_FILTER_UNLOAD_FLAGS Flags
682  )
683 /*++
684 
685 Routine Description:
686 
687  This is the unload routine for this filter driver. This is called
688  when the minifilter is about to be unloaded. We can fail this unload
689  request if this is not a mandatory unloaded indicated by the Flags
690  parameter.
691 
692 Arguments:
693 
694  Flags - Indicating if this is a mandatory unload.
695 
696 Return Value:
697 
698  Returns the final status of this operation.
699 
700 --*/
701 {
702  UNREFERENCED_PARAMETER( Flags );
703 
704  PAGED_CODE();
705 
707  ("[Csq]: CancelSafe!Unload\n") );
708 
709  FltUnregisterFilter( Globals.FilterHandle );
710 
711  FreeGlobals();
712 
713  return STATUS_SUCCESS;
714 }
715 
716 
717 //
718 // Context cleanup routine.
719 //
720 
721 VOID
723  _In_ PFLT_CONTEXT Context,
724  _In_ FLT_CONTEXT_TYPE ContextType
725  )
726 /*++
727 
728 Routine Description:
729 
730  FltMgr calls this routine immediately before it deletes the context.
731 
732 Arguments:
733 
734  Context - Pointer to the minifilter driver's portion of the context.
735 
736  ContextType - Type of context. Must be one of the following values:
737  FLT_FILE_CONTEXT (Microsoft Windows Vista and later only.),
738  FLT_INSTANCE_CONTEXT, FLT_STREAM_CONTEXT, FLT_STREAMHANDLE_CONTEXT,
739  FLT_TRANSACTION_CONTEXT (Windows Vista and later only.), and
740  FLT_VOLUME_CONTEXT
741 
742 Return Value:
743 
744  None.
745 
746 --*/
747 {
748  UNREFERENCED_PARAMETER( Context );
749  UNREFERENCED_PARAMETER( ContextType );
750 
751  PAGED_CODE();
752 
754  ("[Csq]: CancelSafe!ContextCleanup\n") );
755 }
756 
757 //
758 // Instance setup/teardown routines.
759 //
760 
761 NTSTATUS
763  _In_ PCFLT_RELATED_OBJECTS FltObjects,
764  _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
765  _In_ DEVICE_TYPE VolumeDeviceType,
766  _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
767  )
768 /*++
769 
770 Routine Description:
771 
772  This routine is called whenever a new instance is created on a volume. This
773  gives us a chance to decide if we need to attach to this volume or not.
774 
775 Arguments:
776 
777  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
778  opaque handles to this filter, instance and its associated volume.
779 
780  Flags - Flags describing the reason for this attach request.
781 
782  VolumeDeviceType - Device type of the file system volume.
783  Must be one of the following: FILE_DEVICE_CD_ROM_FILE_SYSTEM,
784  FILE_DEVICE_DISK_FILE_SYSTEM, and FILE_DEVICE_NETWORK_FILE_SYSTEM.
785 
786  VolumeFilesystemType - File system type of the volume.
787 
788 Return Value:
789 
790  STATUS_SUCCESS - attach
791  STATUS_FLT_DO_NOT_ATTACH - do not attach
792 
793 --*/
794 {
795  PINSTANCE_CONTEXT InstCtx = NULL;
796  NTSTATUS Status = STATUS_SUCCESS;
797 
798  UNREFERENCED_PARAMETER( Flags );
799  UNREFERENCED_PARAMETER( VolumeDeviceType );
800  UNREFERENCED_PARAMETER( VolumeFilesystemType );
801 
802  PAGED_CODE();
803 
805  ("[Csq]: CancelSafe!InstanceSetup\n") );
806 
807  //
808  // Allocate and initialize the instance context.
809  //
810 
811  Status = FltAllocateContext( FltObjects->Filter,
812  FLT_INSTANCE_CONTEXT,
813  sizeof( INSTANCE_CONTEXT ),
814  NonPagedPool,
815  &InstCtx );
816 
817  if (!NT_SUCCESS( Status )) {
818 
820  ("[Csq]: Failed to allocate instance context (Volume = %p, Instance = %p, Status = 0x%x)\n",
821  FltObjects->Volume,
822  FltObjects->Instance,
823  Status) );
824 
825  goto InstanceSetupCleanup;
826  }
827 
828  Status = FltCbdqInitialize( FltObjects->Instance,
829  &InstCtx->Cbdq,
830  CsqInsertIo,
831  CsqRemoveIo,
833  CsqAcquire,
834  CsqRelease,
836 
837  if (!NT_SUCCESS( Status )) {
838 
840  ("[Csq]: Failed to initialize callback data queue (Volume = %p, Instance = %p, Status = 0x%x)\n",
841  FltObjects->Volume,
842  FltObjects->Instance,
843  Status) );
844 
845  goto InstanceSetupCleanup;
846  }
847 
848  //
849  // Initialize the internal queue head and lock of the cancel safe queue.
850  //
851 
852  InitializeListHead( &InstCtx->QueueHead );
853 
854  ExInitializeFastMutex( &InstCtx->Lock );
855 
856  //
857  // Initialize other members of the instance context.
858  //
859 
860  InstCtx->Instance = FltObjects->Instance;
861 
862  InstCtx->WorkerThreadFlag = 0;
863 
864  KeInitializeEvent( &InstCtx->TeardownEvent, NotificationEvent, FALSE );
865 
866  //
867  // Set the instance context.
868  //
869 
870  Status = FltSetInstanceContext( FltObjects->Instance,
871  FLT_SET_CONTEXT_KEEP_IF_EXISTS,
872  InstCtx,
873  NULL );
874 
875  if (!NT_SUCCESS( Status )) {
876 
878  ("[Csq]: Failed to set instance context (Volume = %p, Instance = %p, Status = 0x%x)\n",
879  FltObjects->Volume,
880  FltObjects->Instance,
881  Status) );
882 
883  goto InstanceSetupCleanup;
884  }
885 
886 
887 InstanceSetupCleanup:
888 
889  if (InstCtx != NULL) {
890 
891  FltReleaseContext( InstCtx );
892  }
893 
894  return Status;
895 }
896 
897 
898 NTSTATUS
900  _In_ PCFLT_RELATED_OBJECTS FltObjects,
901  _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
902  )
903 /*++
904 
905 Routine Description:
906 
907  This is called when an instance is being manually deleted by a
908  call to FltDetachVolume or FilterDetach thereby giving us a
909  chance to fail that detach request.
910 
911 Arguments:
912 
913  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
914  opaque handles to this filter, instance and its associated volume.
915 
916  Flags - Indicating where this detach request came from.
917 
918 Return Value:
919 
920  Returns the status of this operation.
921 
922 --*/
923 {
924  UNREFERENCED_PARAMETER( FltObjects );
925  UNREFERENCED_PARAMETER( Flags );
926 
927  PAGED_CODE();
928 
930  ("[Csq]: CancelSafe!InstanceQueryTeardown\n") );
931 
932  return STATUS_SUCCESS;
933 }
934 
935 
936 VOID
938  _In_ PCFLT_RELATED_OBJECTS FltObjects,
939  _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
940  )
941 /*++
942 
943 Routine Description:
944 
945  This routine is called at the start of instance teardown.
946 
947 Arguments:
948 
949  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
950  opaque handles to this filter, instance and its associated volume.
951 
952  Flags - Reason why this instance is been deleted.
953 
954 Return Value:
955 
956  None.
957 
958 --*/
959 {
960  PINSTANCE_CONTEXT InstCtx = 0;
961  NTSTATUS Status;
962 
963  UNREFERENCED_PARAMETER( FltObjects );
964  UNREFERENCED_PARAMETER( Flags );
965 
966  PAGED_CODE();
967 
969  ("[Csq]: CancelSafe!InstanceTeardownStart\n") );
970 
971  //
972  // Get a pointer to the instance context.
973  //
974 
975  Status = FltGetInstanceContext( FltObjects->Instance,
976  &InstCtx );
977 
978  if (!NT_SUCCESS( Status ))
979  {
980  FLT_ASSERT( !"Instance Context is missing" );
981  return;
982  }
983 
984  //
985  // Disable the insert to the cancel safe queue.
986  //
987 
988  FltCbdqDisable( &InstCtx->Cbdq );
989 
990  //
991  // Remove all callback data from the queue and complete them.
992  //
993 
994  PreReadEmptyQueueAndComplete( InstCtx );
995 
996  //
997  // Signal the worker thread if it is pended.
998  //
999 
1000  KeSetEvent( &InstCtx->TeardownEvent, 0, FALSE );
1001 
1002  //
1003  // Cleanup
1004  //
1005 
1006  FltReleaseContext( InstCtx );
1007 }
1008 
1009 
1010 VOID
1012  _In_ PCFLT_RELATED_OBJECTS FltObjects,
1013  _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags
1014  )
1015 /*++
1016 
1017 Routine Description:
1018 
1019  This routine is called at the end of instance teardown.
1020 
1021 Arguments:
1022 
1023  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
1024  opaque handles to this filter, instance and its associated volume.
1025 
1026  Flags - Reason why this instance is been deleted.
1027 
1028 Return Value:
1029 
1030  None.
1031 
1032 --*/
1033 {
1034  UNREFERENCED_PARAMETER( FltObjects );
1035  UNREFERENCED_PARAMETER( Flags );
1036 
1038  ("[Csq]: CancelSafe!InstanceTeardownComplete\n") );
1039 
1040  PAGED_CODE();
1041 }
1042 
1043 
1044 //
1045 // Cbdq callback routines.
1046 //
1047 
1048 VOID
1050 _IRQL_raises_(APC_LEVEL)
1051 _Requires_lock_not_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
1052 _Acquires_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
1053 CsqAcquire(
1054  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
1055  _Out_ PKIRQL Irql
1056  )
1057 /*++
1058 
1059 Routine Description:
1060 
1061  FltMgr calls this routine to acquire the lock protecting the queue.
1062 
1063 Arguments:
1064 
1065  DataQueue - Supplies a pointer to the queue itself.
1066 
1067  Irql - Returns the previous IRQL if a spinlock is acquired. We do not use
1068  any spinlocks, so we ignore this.
1069 
1070 Return Value:
1071 
1072  None.
1073 
1074 --*/
1075 {
1076  PINSTANCE_CONTEXT InstCtx;
1077 
1079  ("[Csq]: CancelSafe!CsqAcquire\n") );
1080 
1081  //
1082  // Get a pointer to the instance context.
1083  //
1084 
1085  InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );
1086 
1087  //
1088  // Acquire the lock.
1089  //
1090 
1091  ExAcquireFastMutex( &InstCtx->Lock );
1092 
1093  *Irql = 0;
1094 }
1095 
1096 
1097 VOID
1099 _IRQL_requires_min_(APC_LEVEL)
1100 _IRQL_raises_(PASSIVE_LEVEL)
1101 _Requires_lock_held_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
1102 _Releases_lock_((CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq ))->Lock)
1103 CsqRelease(
1104  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
1105  _In_ KIRQL Irql
1106  )
1107 /*++
1108 
1109 Routine Description:
1110 
1111  FltMgr calls this routine to release the lock protecting the queue.
1112 
1113 Arguments:
1114 
1115  DataQueue - Supplies a pointer to the queue itself.
1116 
1117  Irql - Supplies the previous IRQL if a spinlock is acquired. We do not use
1118  any spinlocks, so we ignore this.
1119 
1120 Return Value:
1121 
1122  None.
1123 
1124 --*/
1125 {
1126  PINSTANCE_CONTEXT InstCtx;
1127 
1128  UNREFERENCED_PARAMETER( Irql );
1129 
1131  ("[Csq]: CancelSafe!CsqRelease\n") );
1132 
1133  //
1134  // Get a pointer to the instance context.
1135  //
1136 
1137  InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );
1138 
1139  //
1140  // Release the lock.
1141  //
1142 
1143  ExReleaseFastMutex( &InstCtx->Lock );
1144 }
1145 
1146 
1147 NTSTATUS
1149  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
1150  _In_ PFLT_CALLBACK_DATA Data,
1151  _In_opt_ PVOID Context
1152  )
1153 /*++
1154 
1155 Routine Description:
1156 
1157  FltMgr calls this routine to insert an entry into our pending I/O queue.
1158  The queue is already locked before this routine is called.
1159 
1160 Arguments:
1161 
1162  DataQueue - Supplies a pointer to the queue itself.
1163 
1164  Data - Supplies the callback data for the operation that is being
1165  inserted into the queue.
1166 
1167  Context - Supplies user-defined context information.
1168 
1169 Return Value:
1170 
1171  STATUS_SUCCESS if the function completes successfully. Otherwise a valid
1172  NTSTATUS code is returned.
1173 
1174 --*/
1175 {
1176  PINSTANCE_CONTEXT InstCtx;
1177  PFLT_GENERIC_WORKITEM WorkItem = NULL;
1178  NTSTATUS Status = STATUS_SUCCESS;
1179  BOOLEAN WasQueueEmpty;
1180 
1181  UNREFERENCED_PARAMETER( Context );
1182 
1184  ("[Csq]: CancelSafe!CsqInsertIo\n") );
1185 
1186  //
1187  // Get a pointer to the instance context.
1188  //
1189 
1190  InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );
1191 
1192  //
1193  // Save the queue state before inserting to it.
1194  //
1195 
1196  WasQueueEmpty = IsListEmpty( &InstCtx->QueueHead );
1197 
1198  //
1199  // Insert the callback data entry into the queue.
1200  //
1201 
1202  InsertTailList( &InstCtx->QueueHead,
1203  &Data->QueueLinks );
1204 
1205  //
1206  // Queue a work item if no worker thread present.
1207  //
1208 
1209  if (WasQueueEmpty &&
1210  InterlockedIncrement( &InstCtx->WorkerThreadFlag ) == 1) {
1211 
1212  WorkItem = FltAllocateGenericWorkItem();
1213 
1214  if (WorkItem) {
1215 
1216  Status = FltQueueGenericWorkItem( WorkItem,
1217  InstCtx->Instance,
1219  DelayedWorkQueue,
1220  InstCtx->Instance );
1221 
1222  if (!NT_SUCCESS( Status )) {
1223 
1225  ("[Csq]: Failed to queue the work item (Status = 0x%x)\n",
1226  Status) );
1227 
1228  FltFreeGenericWorkItem( WorkItem );
1229  }
1230 
1231  } else {
1232 
1233  Status = STATUS_INSUFFICIENT_RESOURCES;
1234  }
1235 
1236  if (!NT_SUCCESS( Status )) {
1237 
1238  //
1239  // If we failed to queue a workitem we need to
1240  // decrement the worker thread flag. If we did
1241  // not decrement it future queue insertions would
1242  // not trigger a workitem and requests added to
1243  // the queue would be orphaned. We can safely
1244  // decrement the flag here because we are
1245  // guaranteed that no worker routine is currently
1246  // running and that the queue is currently locked.
1247  //
1248 
1249  InterlockedDecrement( &InstCtx->WorkerThreadFlag );
1250  NT_ASSERT( InstCtx->WorkerThreadFlag == 0 );
1251 
1252  //
1253  // Remove the callback data that was inserted into the queue.
1254  //
1255 
1256  RemoveTailList( &InstCtx->QueueHead );
1257  }
1258  }
1259 
1260  return Status;
1261 }
1262 
1263 
1264 VOID
1266  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
1267  _In_ PFLT_CALLBACK_DATA Data
1268  )
1269 /*++
1270 
1271 Routine Description:
1272 
1273  FltMgr calls this routine to remove an entry from our pending I/O queue.
1274  The queue is already locked before this routine is called.
1275 
1276 Arguments:
1277 
1278  DataQueue - Supplies a pointer to the queue itself.
1279 
1280  Data - Supplies the callback data that is to be removed.
1281 
1282 Return Value:
1283 
1284  None.
1285 
1286 --*/
1287 {
1288  UNREFERENCED_PARAMETER( DataQueue );
1289 
1291  ("[Csq]: CancelSafe!CsqRemoveIo\n") );
1292 
1293  //
1294  // Remove the callback data entry from the queue.
1295  //
1296 
1297  RemoveEntryList( &Data->QueueLinks );
1298 }
1299 
1300 
1301 PFLT_CALLBACK_DATA
1303  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
1304  _In_opt_ PFLT_CALLBACK_DATA Data,
1305  _In_opt_ PVOID PeekContext
1306  )
1307 /*++
1308 
1309 Routine Description:
1310 
1311  FltMgr calls this routine to look for an entry on our pending I/O queue.
1312  The queue is already locked before this routine is called.
1313 
1314 Arguments:
1315 
1316  DataQueue - Supplies a pointer to the queue itself.
1317 
1318  Data - Supplies the callback data we should start our search from.
1319  If this is NULL, we start at the beginning of the list.
1320 
1321  PeekContext - Supplies user-defined context information.
1322 
1323 Return Value:
1324 
1325  A pointer to the next callback data structure, or NULL.
1326 
1327 --*/
1328 {
1329  PINSTANCE_CONTEXT InstCtx;
1330  PLIST_ENTRY NextEntry;
1331  PFLT_CALLBACK_DATA NextData;
1332 
1333  UNREFERENCED_PARAMETER( PeekContext );
1334 
1336  ("[Csq]: CancelSafe!CsqPeekNextIo\n") );
1337 
1338  //
1339  // Get a pointer to the instance context.
1340  //
1341 
1342  InstCtx = CONTAINING_RECORD( DataQueue, INSTANCE_CONTEXT, Cbdq );
1343 
1344  //
1345  // If the supplied callback "Data" is NULL, the "NextIo" is the first entry
1346  // in the queue; or it is the next list entry in the queue.
1347  //
1348 
1349  if (Data == NULL) {
1350 
1351  NextEntry = InstCtx->QueueHead.Flink;
1352 
1353  } else {
1354 
1355  NextEntry = Data->QueueLinks.Flink;
1356  }
1357 
1358  //
1359  // Return NULL if we hit the end of the queue or the queue is empty.
1360  //
1361 
1362  if (NextEntry == &InstCtx->QueueHead) {
1363 
1364  return NULL;
1365  }
1366 
1367  NextData = CONTAINING_RECORD( NextEntry, FLT_CALLBACK_DATA, QueueLinks );
1368 
1369  return NextData;
1370 }
1371 
1372 
1373 VOID
1375  _In_ PFLT_CALLBACK_DATA_QUEUE DataQueue,
1376  _Inout_ PFLT_CALLBACK_DATA Data
1377  )
1378 /*++
1379 
1380 Routine Description:
1381 
1382  FltMgr calls this routine to complete an operation as cancelled that was
1383  previously pended. The queue is already locked before this routine is called.
1384 
1385 Arguments:
1386 
1387  DataQueue - Supplies a pointer to the queue itself.
1388 
1389  Data - Supplies the callback data that is to be canceled.
1390 
1391 Return Value:
1392 
1393  None.
1394 
1395 --*/
1396 {
1397  PQUEUE_CONTEXT QueueCtx;
1398 
1399  UNREFERENCED_PARAMETER( DataQueue );
1400 
1402  ("[Csq]: CancelSafe!CsqCompleteCanceledIo\n") );
1403 
1404  QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];
1405 
1406  //
1407  // Just complete the operation as canceled.
1408  //
1409 
1410  Data->IoStatus.Status = STATUS_CANCELLED;
1411  Data->IoStatus.Information = 0;
1412 
1413  FltCompletePendedPreOperation( Data,
1414  FLT_PREOP_COMPLETE,
1415  0 );
1416 
1417  //
1418  // Free the extra storage that was allocated for this canceled I/O.
1419  //
1420 
1421  ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside,
1422  QueueCtx );
1423 }
1424 
1425 
1426 FLT_PREOP_CALLBACK_STATUS
1428  _Inout_ PFLT_CALLBACK_DATA Data,
1429  _In_ PCFLT_RELATED_OBJECTS FltObjects,
1430  _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
1431  )
1432 /*++
1433 
1434 Routine Description:
1435 
1436  Handle pre-read.
1437 
1438 Arguments:
1439 
1440  Data - Pointer to the filter callbackData that is passed to us.
1441 
1442  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
1443  opaque handles to this filter, instance, its associated volume and
1444  file object.
1445 
1446  CompletionContext - The context for the completion routine for this
1447  operation.
1448 
1449 Return Value:
1450 
1451  The return value is the status of the operation.
1452 
1453 --*/
1454 {
1455 
1456  PINSTANCE_CONTEXT InstCtx = NULL;
1457  PQUEUE_CONTEXT QueueCtx = NULL;
1458  PFLT_FILE_NAME_INFORMATION NameInfo = NULL;
1459  NTSTATUS CbStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
1460  NTSTATUS Status;
1461 
1462  UNREFERENCED_PARAMETER( CompletionContext );
1463 
1465  ("[Csq]: CancelSafe!PreRead\n") );
1466 
1467  //
1468  // Skip IRP_PAGING_IO, IRP_SYNCHRONOUS_PAGING_IO and
1469  // TopLevelIrp.
1470  //
1471 
1472  if ((Data->Iopb->IrpFlags & IRP_PAGING_IO) ||
1473  (Data->Iopb->IrpFlags & IRP_SYNCHRONOUS_PAGING_IO) ||
1474  IoGetTopLevelIrp()) {
1475 
1476  return FLT_PREOP_SUCCESS_NO_CALLBACK;
1477  }
1478 
1479  //
1480  // Get and parse the file name
1481  //
1482 
1483  Status = FltGetFileNameInformation( Data,
1484  FLT_FILE_NAME_NORMALIZED
1485  | FLT_FILE_NAME_QUERY_DEFAULT,
1486  &NameInfo );
1487 
1488  if (!NT_SUCCESS( Status )) {
1489 
1491  ("[Csq]: Failed to get filename (Status = 0x%x)\n",
1492  Status) );
1493 
1494  goto PreReadCleanup;
1495  }
1496 
1497  Status = FltParseFileNameInformation( NameInfo );
1498 
1499  if (!NT_SUCCESS( Status )) {
1500 
1502  ("[Csq]: Failed to parse filename (Name = %wZ, Status = 0x%x)\n",
1503  &NameInfo->Name,
1504  Status) );
1505 
1506  goto PreReadCleanup;
1507  }
1508 
1509  //
1510  // Compare to see if this file I/O is to be pended.
1511  //
1512 
1513  if (!RtlPrefixUnicodeString( &Globals.MappingPath, &NameInfo->ParentDir, TRUE )) {
1514 
1515  goto PreReadCleanup;
1516  }
1517 
1518  //
1519  // Since Fast I/O operations cannot be queued, we could return
1520  // FLT_PREOP_SUCCESS_NO_CALLBACK at this point. In this sample,
1521  // we disallow Fast I/O for this magic file in order to force an IRP
1522  // to be sent to us again. The purpose of doing that is to demonstrate
1523  // the cancel safe queue, which may not be true in the real world.
1524  //
1525 
1526  if (!FLT_IS_IRP_OPERATION( Data )) {
1527 
1528  CbStatus = FLT_PREOP_DISALLOW_FASTIO;
1529  goto PreReadCleanup;
1530  }
1531 
1532  //
1533  // Allocate a context for each I/O to be inserted into the queue.
1534  //
1535 
1536  QueueCtx = ExAllocateFromNPagedLookasideList( &Globals.QueueContextLookaside );
1537 
1538  if (QueueCtx == NULL) {
1539 
1541  ("[Csq]: Failed to allocate from NPagedLookasideList (Status = 0x%x)\n",
1542  Status) );
1543 
1544  goto PreReadCleanup;
1545  }
1546 
1547  RtlZeroMemory(QueueCtx, sizeof(QUEUE_CONTEXT));
1548 
1549  //
1550  // Get the instance context.
1551  //
1552 
1553  Status = FltGetInstanceContext( FltObjects->Instance,
1554  &InstCtx );
1555 
1556  if (!NT_SUCCESS( Status )) {
1557 
1558  FLT_ASSERT( !"Instance context is missing" );
1559  goto PreReadCleanup;
1560  }
1561 
1562  //
1563  // Set the queue context
1564  //
1565 
1566  Data->QueueContext[0] = (PVOID) QueueCtx;
1567  Data->QueueContext[1] = NULL;
1568 
1569  //
1570  // Insert the callback data into the cancel safe queue
1571  //
1572 
1573  Status = FltCbdqInsertIo( &InstCtx->Cbdq,
1574  Data,
1575  &QueueCtx->CbdqIoContext,
1576  0 );
1577 
1578  if (Status == STATUS_SUCCESS) {
1579 
1580  //
1581  // In general, we can create a worker thread here as long as we can
1582  // correctly handle the insert/remove race conditions b/w multi threads.
1583  // In this sample, the worker thread creation is done in CsqInsertIo.
1584  // This is a simpler solution because CsqInsertIo is atomic with
1585  // respect to other CsqXxxIo callback routines.
1586  //
1587 
1588  CbStatus = FLT_PREOP_PENDING;
1589 
1590  } else {
1591 
1593  ("[Csq]: Failed to insert into cbdq (Status = 0x%x)\n",
1594  Status) );
1595  }
1596 
1597 PreReadCleanup:
1598 
1599  //
1600  // Clean up
1601  //
1602 
1603  if (QueueCtx && CbStatus != FLT_PREOP_PENDING) {
1604 
1605  ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside, QueueCtx );
1606  }
1607 
1608  if (NameInfo) {
1609 
1610  FltReleaseFileNameInformation( NameInfo );
1611  }
1612 
1613  if (InstCtx) {
1614 
1615  FltReleaseContext( InstCtx );
1616  }
1617 
1618  return CbStatus;
1619 }
1620 
1621 
1622 VOID
1624  _In_ PFLT_GENERIC_WORKITEM WorkItem,
1625  _In_ PFLT_FILTER Filter,
1626  _In_ PVOID Context
1627  )
1628 /*++
1629 
1630 Routine Description:
1631 
1632  This WorkItem routine is called in the system thread context to process
1633  all the pended I/O in this mini filter's cancel safe queue. For each I/O
1634  in the queue, it completes the I/O after pending the operation for a
1635  period of time. The thread exits when the queue is empty.
1636 
1637 Arguments:
1638 
1639  WorkItem - Unused.
1640 
1641  Filter - Unused.
1642 
1643  Context - Context information.
1644 
1645 Return Value:
1646 
1647  None.
1648 
1649 --*/
1650 {
1651  PINSTANCE_CONTEXT InstCtx = NULL;
1652  PFLT_CALLBACK_DATA Data;
1653  PFLT_INSTANCE Instance = (PFLT_INSTANCE)Context;
1654  PQUEUE_CONTEXT QueueCtx;
1655  NTSTATUS Status;
1656  FLT_PREOP_CALLBACK_STATUS callbackStatus;
1657 
1658  UNREFERENCED_PARAMETER( WorkItem );
1659  UNREFERENCED_PARAMETER( Filter );
1660 
1662  ("[Csq]: CancelSafe!PreReadWorkItemRoutine\n") );
1663 
1664  //
1665  // Get a pointer to the instance context.
1666  //
1667 
1668  Status = FltGetInstanceContext( Instance,
1669  &InstCtx );
1670 
1671  if (!NT_SUCCESS( Status ))
1672  {
1673  FLT_ASSERT( !"Instance Context is missing" );
1674  return;
1675  }
1676 
1677  //
1678  // Process all the pended I/O in the cancel safe queue
1679  //
1680 
1681  for (;;) {
1682 
1683  callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
1684 
1685  PreReadPendIo( InstCtx );
1686 
1687  //
1688  // WorkerThreadFlag >= 1;
1689  // Here we reduce it to 1.
1690  //
1691 
1692  InterlockedExchange( &InstCtx->WorkerThreadFlag, 1 );
1693 
1694  //
1695  // Remove an I/O from the cancel safe queue.
1696  //
1697 
1698  Data = FltCbdqRemoveNextIo( &InstCtx->Cbdq,
1699  NULL);
1700 
1701  if (Data) {
1702 
1703  QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];
1704 
1705  PreReadProcessIo( Data );
1706 
1707  //
1708  // Check to see if we need to lock the user buffer.
1709  //
1710  // If the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is set we don't
1711  // have to lock the buffer because its already a system buffer.
1712  //
1713  // If the MdlAddress is NULL and the buffer is a user buffer,
1714  // then we have to construct one in order to look at the buffer.
1715  //
1716  // If the length of the buffer is zero there is nothing to read,
1717  // so we cannot construct a MDL.
1718  //
1719 
1720  if (!FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) &&
1721  Data->Iopb->Parameters.Read.MdlAddress == NULL &&
1722  Data->Iopb->Parameters.Read.Length > 0) {
1723 
1724  Status = FltLockUserBuffer( Data );
1725 
1726  if (!NT_SUCCESS( Status )) {
1727 
1728  //
1729  // If could not lock the user buffer we cannot
1730  // allow the IO to go below us. Because we are
1731  // in a different VA space and the buffer is a
1732  // user mode address, we will either fault or
1733  // corrpt data
1734  //
1735 
1737  ("[Csq]: Failed to lock user buffer (Status = 0x%x)\n",
1738  Status) );
1739 
1740  callbackStatus = FLT_PREOP_COMPLETE;
1741  Data->IoStatus.Status = Status;
1742  }
1743  }
1744 
1745  //
1746  // Complete the I/O
1747  //
1748 
1749  FltCompletePendedPreOperation( Data,
1750  callbackStatus,
1751  NULL );
1752 
1753  //
1754  // Free the extra storage that was allocated for this I/O.
1755  //
1756 
1757  ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside,
1758  QueueCtx );
1759 
1760  } else {
1761 
1762  //
1763  // At this moment it is possible that a new IO is being inserted
1764  // into the queue in the CsqInsertIo routine. Now that the queue is
1765  // empty, CsqInsertIo needs to make a decision on whether to create
1766  // a new worker thread. The decision is based on the race between
1767  // the InterlockedIncrement in CsqInsertIo and the
1768  // InterlockedDecrement as below. There are two situations:
1769  //
1770  // (1) If the decrement executes earlier before the increment,
1771  // the flag will be decremented to 0 so this worker thread
1772  // will return. Then CsqInsertIo will increment the flag
1773  // from 0 to 1, and therefore create a new worker thread.
1774  // (2) If the increment executes earlier before the decrement,
1775  // the flag will be first incremented to 2 in CsqInsertIo
1776  // so a new worker thread will not be satisfied. Then the
1777  // decrement as below will lower the flag down to 1, and
1778  // therefore continue this worker thread.
1779  //
1780 
1781  if (InterlockedDecrement( &InstCtx->WorkerThreadFlag ) == 0) {
1782 
1783  break;
1784  }
1785 
1786  }
1787  }
1788 
1789  //
1790  // Clean up
1791  //
1792 
1793  FltReleaseContext(InstCtx);
1794 
1795  FltFreeGenericWorkItem(WorkItem);
1796 }
1797 
1798 
1799 NTSTATUS
1801  _In_ PINSTANCE_CONTEXT InstanceContext
1802  )
1803 /*++
1804 
1805 Routine Description:
1806 
1807  This routine waits for a period of time or until the instance is
1808  torndown.
1809 
1810 Arguments:
1811 
1812  InstanceContext - Supplies a pointer to the instance context.
1813 
1814 Return Value:
1815 
1816  The return value is the status of the operation.
1817 
1818 --*/
1819 {
1820  LARGE_INTEGER DueTime;
1821  NTSTATUS Status;
1822 
1823  //
1824  // Delay or get signaled if the instance is torndown.
1825  //
1826 
1827  DueTime.QuadPart = (LONGLONG) - Globals.TimeDelay;
1828 
1829  Status = KeWaitForSingleObject( &InstanceContext->TeardownEvent,
1830  Executive,
1831  KernelMode,
1832  FALSE,
1833  &DueTime );
1834 
1835  return Status;
1836 }
1837 
1838 
1839 NTSTATUS
1841  _Inout_ PFLT_CALLBACK_DATA Data
1842  )
1843 /*++
1844 
1845 Routine Description:
1846 
1847  This routine process the I/O that was removed from the queue.
1848 
1849 Arguments:
1850 
1851  Data - Supplies the callback data that was removed from the queue.
1852 
1853 Return Value:
1854 
1855  The return value is the status of the operation.
1856 
1857 --*/
1858 {
1859  UNREFERENCED_PARAMETER( Data );
1860 
1861  return STATUS_SUCCESS;
1862 }
1863 
1864 
1865 VOID
1867  _In_ PINSTANCE_CONTEXT InstanceContext
1868  )
1869 /*++
1870 
1871 Routine Description:
1872 
1873  This routine empties the cancel safe queue and complete all the
1874  pended pre-read operations.
1875 
1876 Arguments:
1877 
1878  InstanceContext - Supplies a pointer to the instance context.
1879 
1880 Return Value:
1881 
1882  None.
1883 
1884 --*/
1885 {
1886  NTSTATUS Status;
1887  FLT_PREOP_CALLBACK_STATUS callbackStatus;
1888  PFLT_CALLBACK_DATA Data;
1889  PQUEUE_CONTEXT QueueCtx;
1890 
1891  do {
1892 
1893  callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
1894 
1895  Data = FltCbdqRemoveNextIo( &InstanceContext->Cbdq,
1896  NULL );
1897 
1898  if (Data) {
1899 
1900  QueueCtx = (PQUEUE_CONTEXT) Data->QueueContext[0];
1901 
1902  //
1903  // Check to see if we need to lock the user buffer.
1904  //
1905  // If the FLTFL_CALLBACK_DATA_SYSTEM_BUFFER flag is set we don't
1906  // have to lock the buffer because its already a system buffer.
1907  //
1908  // If the MdlAddress is NULL and the buffer is a user buffer,
1909  // then we have to construct one in order to look at the buffer.
1910  //
1911  // If the length of the buffer is zero there is nothing to read,
1912  // so we cannot construct a MDL.
1913  //
1914 
1915  if (!FlagOn(Data->Flags, FLTFL_CALLBACK_DATA_SYSTEM_BUFFER) &&
1916  Data->Iopb->Parameters.Read.MdlAddress == NULL &&
1917  Data->Iopb->Parameters.Read.Length > 0) {
1918 
1919  Status = FltLockUserBuffer( Data );
1920 
1921  if (!NT_SUCCESS( Status )) {
1922 
1923  //
1924  // If could not lock the user buffer we cannot
1925  // allow the IO to go below us. Because we are
1926  // in a different VA space and the buffer is a
1927  // user mode address, we will either fault or
1928  // corrpt data
1929  //
1930 
1931  callbackStatus = FLT_PREOP_COMPLETE;
1932  Data->IoStatus.Status = Status;
1933  }
1934  }
1935 
1936  FltCompletePendedPreOperation( Data,
1937  callbackStatus,
1938  NULL );
1939 
1940  ExFreeToNPagedLookasideList( &Globals.QueueContextLookaside,
1941  QueueCtx );
1942  }
1943 
1944  } while (Data);
1945 }
1946 
#define CSQ_KEY_NAME_DELAY
Definition: cancelSafe.c:56
KEVENT TeardownEvent
Definition: cancelSafe.c:106
FLT_REGISTRATION FilterRegistration
Definition: cancelSafe.c:315
NTSTATUS CsqInsertIo(_In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, _In_ PFLT_CALLBACK_DATA Data, _In_opt_ PVOID Context)
Definition: cancelSafe.c:1148
_In_ PLARGE_INTEGER _In_ ULONG Length
struct _INSTANCE_CONTEXT INSTANCE_CONTEXT
#define IRP_MJ_READ
Definition: mspyLog.h:287
#define INSTANCE_CONTEXT_TAG
Definition: cancelSafe.c:45
DRIVER_INITIALIZE DriverEntry
Definition: cancelSafe.c:140
VOID CsqRemoveIo(_In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, _In_ PFLT_CALLBACK_DATA Data)
Definition: cancelSafe.c:1265
FORCEINLINE VOID _Acquires_lock_(_Global_critical_region_) AvAcquireResourceExclusive(_Inout_ _Acquires_exclusive_lock_(*Resource) PERESOURCE Resource)
#define CSQ_TRACE_PRE_READ
Definition: cancelSafe.c:33
NTSTATUS PreReadPendIo(_In_ PINSTANCE_CONTEXT InstanceContext)
Definition: cancelSafe.c:1800
#define IRP_SYNCHRONOUS_PAGING_IO
Definition: mspyLog.h:384
#define CSQ_DEFAULT_MAPPING_PATH
Definition: cancelSafe.c:55
RtlCopyMemory(OutputStringBuffer, TempMappingBuffer->Data, OutputString->MaximumLength)
#define DebugTrace(Level, Data)
Definition: cancelSafe.c:36
UNICODE_STRING MappingPath
Definition: cancelSafe.c:119
_In_opt_ PFILE_OBJECT _In_opt_ PFLT_INSTANCE Instance
Definition: nc.h:493
VOID CsqCompleteCanceledIo(_In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, _Inout_ PFLT_CALLBACK_DATA Data)
Definition: cancelSafe.c:1374
NTSTATUS PreReadProcessIo(_Inout_ PFLT_CALLBACK_DATA Data)
Definition: cancelSafe.c:1840
struct _CSQ_GLOBAL_DATA CSQ_GLOBAL_DATA
FLT_ASSERT(IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject))
return TRUE
VOID InstanceTeardownStart(_In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags)
Definition: cancelSafe.c:937
VOID PreReadWorkItemRoutine(_In_ PFLT_GENERIC_WORKITEM WorkItem, _In_ PFLT_FILTER Filter, _In_ PVOID Context)
Definition: cancelSafe.c:1623
CONST FLT_OPERATION_REGISTRATION Callbacks[]
#define IRP_PAGING_IO
Definition: mspyLog.h:382
#define CSQ_MAX_PATH_LENGTH
Definition: cancelSafe.c:59
NcLoadRegistryStringRetry KeyValuePartialInformation
Definition: ncinit.c:53
FLT_PREOP_CALLBACK_STATUS PreRead(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext)
Definition: cancelSafe.c:1427
PFLT_CALLBACK_DATA CsqPeekNextIo(_In_ PFLT_CALLBACK_DATA_QUEUE DataQueue, _In_opt_ PFLT_CALLBACK_DATA Data, _In_opt_ PVOID PeekContext)
Definition: cancelSafe.c:1302
#define CSQ_TRACE_CONTEXT_CALLBACK
Definition: cancelSafe.c:31
volatile LONG WorkerThreadFlag
Definition: cancelSafe.c:100
VOID PreReadEmptyQueueAndComplete(_In_ PINSTANCE_CONTEXT InstanceContext)
Definition: cancelSafe.c:1866
NTSTATUS Unload(_In_ FLT_FILTER_UNLOAD_FLAGS Flags)
Definition: cancelSafe.c:680
#define CSQ_TRACE_ERROR
Definition: cancelSafe.c:28
_In_ BOOLEAN _Out_ PFILE_BASIC_INFORMATION Buffer
#define FlagOn(_F, _SF)
Definition: minispy.h:247
const FLT_CONTEXT_REGISTRATION ContextRegistration[]
Definition: cancelSafe.c:300
LIST_ENTRY QueueHead
Definition: cancelSafe.c:93
UNREFERENCED_PARAMETER(FileObject)
#define CSQ_DEFAULT_TIME_DELAY
Definition: cancelSafe.c:54
#define CSQ_TRACE_LOAD_UNLOAD
Definition: cancelSafe.c:29
NcLoadRegistryStringRetry NULL
Definition: ncinit.c:53
NTSTATUS SetConfiguration(_In_ PUNICODE_STRING RegistryPath)
Definition: cancelSafe.c:461
struct _INSTANCE_CONTEXT * PINSTANCE_CONTEXT
FLT_CALLBACK_DATA_QUEUE Cbdq
Definition: cancelSafe.c:92
NPAGED_LOOKASIDE_LIST QueueContextLookaside
Definition: cancelSafe.c:117
FLT_CALLBACK_DATA_QUEUE_IO_CONTEXT CbdqIoContext
Definition: cancelSafe.c:72
VOID InstanceTeardownComplete(_In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_TEARDOWN_FLAGS Flags)
Definition: cancelSafe.c:1011
PAGED_CODE()
struct _QUEUE_CONTEXT QUEUE_CONTEXT
if(Status !=STATUS_BUFFER_TOO_SMALL &&Status !=STATUS_BUFFER_OVERFLOW)
Definition: ncinit.c:65
IoStatus Status
PFLT_INSTANCE Instance
Definition: cancelSafe.c:86
#define CSQ_TRACE_INSTANCE_CALLBACK
Definition: cancelSafe.c:30
FORCEINLINE VOID _Releases_lock_(_Global_critical_region_) _Requires_lock_held_(_Global_critical_region_) AvReleaseResource(_Inout_ _Requires_lock_held_(*Resource) _Releases_lock_(*Resource) PERESOURCE Resource)
FAST_MUTEX Lock
Definition: cancelSafe.c:94
VOID _IRQL_requires_max_(VOID_IRQL_requires_max_(APC_LEVEL) _IRQL_requires_min_() _IRQL_raises_(PASSIVE_LEVEL) _Requires_lock_held_((CONTAINING_RECORD(DataQueue APC_LEVEL)
Definition: cancelSafe.c:194
#define QUEUE_CONTEXT_TAG
Definition: cancelSafe.c:46
_Requires_lock_held_(_Global_critical_region_)
Definition: DataStore.c:38
LONGLONG TimeDelay
Definition: cancelSafe.c:123
#define CSQ_KEY_NAME_PATH
Definition: cancelSafe.c:57
#define CSQ_STRING_TAG
Definition: cancelSafe.c:48
NTSTATUS InstanceSetup(_In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_SETUP_FLAGS Flags, _In_ DEVICE_TYPE VolumeDeviceType, _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType)
Definition: cancelSafe.c:762
#define CSQ_TRACE_CBDQ_CALLBACK
Definition: cancelSafe.c:32
#define CSQ_KEY_NAME_DEBUG_LEVEL
Definition: cancelSafe.c:58
VOID FreeGlobals()
Definition: cancelSafe.c:646
NTSTATUS InstanceQueryTeardown(_In_ PCFLT_RELATED_OBJECTS FltObjects, _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
Definition: cancelSafe.c:899
PFLT_FILTER FilterHandle
Definition: cancelSafe.c:115
VOID ContextCleanup(_In_ PFLT_CONTEXT Context, _In_ FLT_CONTEXT_TYPE ContextType)
Definition: cancelSafe.c:722
CSQ_GLOBAL_DATA Globals
Definition: cancelSafe.c:133
struct _QUEUE_CONTEXT * PQUEUE_CONTEXT

Social Network


Services Overview

Architect, implement and test file system filter drivers for a wide range of functionality. We can offer several levels of assistance to meet your specific.

Contact Us

You are welcome to contact us for salse or partnership.

Sales: sales@easefilter.com
Support: support@easefilter.com
Info: info@easefilter.com