2 * PROJECT: Filesystem Filter Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/filters/fltmgr/Filter.c
5 * PURPOSE: Handles registration of mini filters
6 * PROGRAMMERS: Ged Murphy (gedmurphy@reactos.org)
9 /* INCLUDES ******************************************************************/
12 #include "fltmgrint.h"
19 /* DATA *********************************************************************/
21 #define SERVICES_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
22 #define MAX_KEY_LENGTH 0x200
25 FltpStartingToDrainObject(
26 _Inout_ PFLT_OBJECT Object
30 FltpMiniFilterDriverUnload(
36 _In_ PFLT_FILTER Filter
,
37 _Inout_ PUNICODE_STRING AltitudeString
41 /* EXPORTED FUNCTIONS ******************************************************/
45 FltLoadFilter(_In_ PCUNICODE_STRING FilterName
)
47 UNICODE_STRING DriverServiceName
;
48 UNICODE_STRING ServicesKey
;
49 CHAR Buffer
[MAX_KEY_LENGTH
];
51 /* Setup the base services key */
52 RtlInitUnicodeString(&ServicesKey
, SERVICES_KEY
);
54 /* Initialize the string data */
55 DriverServiceName
.Length
= 0;
56 DriverServiceName
.Buffer
= (PWCH
)Buffer
;
57 DriverServiceName
.MaximumLength
= MAX_KEY_LENGTH
;
59 /* Create the full service key for this filter */
60 RtlCopyUnicodeString(&DriverServiceName
, &ServicesKey
);
61 RtlAppendUnicodeStringToString(&DriverServiceName
, FilterName
);
63 /* Ask the kernel to load it for us */
64 return ZwLoadDriver(&DriverServiceName
);
69 FltUnloadFilter(_In_ PCUNICODE_STRING FilterName
)
72 //FIXME: This is a temp hack, it needs properly implementing
75 UNICODE_STRING DriverServiceName
;
76 UNICODE_STRING ServicesKey
;
77 CHAR Buffer
[MAX_KEY_LENGTH
];
79 /* Setup the base services key */
80 RtlInitUnicodeString(&ServicesKey
, SERVICES_KEY
);
82 /* Initialize the string data */
83 DriverServiceName
.Length
= 0;
84 DriverServiceName
.Buffer
= (PWCH
)Buffer
;
85 DriverServiceName
.MaximumLength
= MAX_KEY_LENGTH
;
87 /* Create the full service key for this filter */
88 RtlCopyUnicodeString(&DriverServiceName
, &ServicesKey
);
89 RtlAppendUnicodeStringToString(&DriverServiceName
, FilterName
);
90 return ZwUnloadDriver(&DriverServiceName
);
95 FltRegisterFilter(_In_ PDRIVER_OBJECT DriverObject
,
96 _In_
const FLT_REGISTRATION
*Registration
,
97 _Out_ PFLT_FILTER
*RetFilter
)
99 PFLT_OPERATION_REGISTRATION Callbacks
;
101 ULONG CallbackBufferSize
;
102 ULONG FilterBufferSize
;
109 /* Make sure we're targeting the correct major revision */
110 if ((Registration
->Version
& 0xFF00) != FLT_MAJOR_VERSION
)
112 return STATUS_INVALID_PARAMETER
;
115 /* Make sure our namespace callbacks are valid */
116 if ((!Registration
->GenerateFileNameCallback
&& Registration
->NormalizeNameComponentCallback
) ||
117 (!Registration
->NormalizeNameComponentCallback
&& Registration
->NormalizeContextCleanupCallback
))
119 return STATUS_INVALID_PARAMETER
;
122 /* Count the number of operations that were requested */
123 Callbacks
= (PFLT_OPERATION_REGISTRATION
)Registration
->OperationRegistration
;
128 /* Bail when we find the last one */
129 if (Callbacks
->MajorFunction
== IRP_MJ_OPERATION_END
)
132 /* Move to the next item */
136 /* Calculate the buffer sizes */
137 CallbackBufferSize
= Count
* sizeof(FLT_OPERATION_REGISTRATION
);
138 FilterBufferSize
= sizeof(FLT_FILTER
) +
140 DriverObject
->DriverExtension
->ServiceKeyName
.Length
;
142 /* Allocate a buffer to hold our filter data */
143 Filter
= ExAllocatePoolWithTag(NonPagedPool
,
146 if (Filter
== NULL
) return STATUS_INSUFFICIENT_RESOURCES
;
147 RtlZeroMemory(Filter
, FilterBufferSize
);
149 /* Find the end of the fixed struct */
150 Ptr
= (PCHAR
)(Filter
+ 1);
152 /* Store a copy of the driver object of this filter */
153 Filter
->DriverObject
= DriverObject
;
155 /* Initialize the base object data */
156 Filter
->Base
.Flags
= FLT_OBFL_TYPE_FILTER
;
157 Filter
->Base
.PointerCount
= 1;
158 FltpExInitializeRundownProtection(&Filter
->Base
.RundownRef
);
159 FltObjectReference(&Filter
->Base
);
161 /* Set the callback addresses */
162 Filter
->FilterUnload
= Registration
->FilterUnloadCallback
;
163 Filter
->InstanceSetup
= Registration
->InstanceSetupCallback
;
164 Filter
->InstanceQueryTeardown
= Registration
->InstanceQueryTeardownCallback
;
165 Filter
->InstanceTeardownStart
= Registration
->InstanceTeardownStartCallback
;
166 Filter
->InstanceTeardownComplete
= Registration
->InstanceTeardownCompleteCallback
;
167 Filter
->GenerateFileName
= Registration
->GenerateFileNameCallback
;
168 Filter
->NormalizeNameComponent
= Registration
->NormalizeNameComponentCallback
;
169 Filter
->NormalizeContextCleanup
= Registration
->NormalizeContextCleanupCallback
;
171 /* Initialize the instance list */
172 ExInitializeResourceLite(&Filter
->InstanceList
.rLock
);
173 InitializeListHead(&Filter
->InstanceList
.rList
);
174 Filter
->InstanceList
.rCount
= 0;
176 ExInitializeFastMutex(&Filter
->ActiveOpens
.mLock
);
177 InitializeListHead(&Filter
->ActiveOpens
.mList
);
178 Filter
->ActiveOpens
.mCount
= 0;
180 ExInitializeFastMutex(&Filter
->ConnectionList
.mLock
);
181 InitializeListHead(&Filter
->ConnectionList
.mList
);
182 Filter
->ConnectionList
.mCount
= 0;
184 /* Initialize the usermode port list */
185 ExInitializeFastMutex(&Filter
->PortList
.mLock
);
186 InitializeListHead(&Filter
->PortList
.mList
);
187 Filter
->PortList
.mCount
= 0;
189 /* We got this far, assume success from here */
190 Status
= STATUS_SUCCESS
;
192 /* Check if the caller requested any context data */
193 if (Registration
->ContextRegistration
)
195 /* Register the contexts for this filter */
196 Status
= FltpRegisterContexts(Filter
, Registration
->ContextRegistration
);
197 if (NT_SUCCESS(Status
))
203 /* Check if the caller is registering any callbacks */
204 if (Registration
->OperationRegistration
)
206 /* The callback data comes after the fixed struct */
207 Filter
->Operations
= (PFLT_OPERATION_REGISTRATION
)Ptr
;
208 Ptr
+= (Count
* sizeof(FLT_OPERATION_REGISTRATION
));
210 /* Tag the operation data onto the end of the filter data */
211 RtlCopyMemory(Filter
->Operations
, Registration
->OperationRegistration
, CallbackBufferSize
);
213 /* walk through the requested callbacks */
214 for (Callbacks
= Filter
->Operations
;
215 Callbacks
->MajorFunction
!= IRP_MJ_OPERATION_END
;
218 // http://fsfilters.blogspot.co.uk/2011/03/how-file-system-filters-attach-to_17.html
219 /* Check if this is an attach to a volume */
220 if (Callbacks
->MajorFunction
== IRP_MJ_VOLUME_MOUNT
)
222 Filter
->PreVolumeMount
= Callbacks
->PreOperation
;
223 Filter
->PostVolumeMount
= Callbacks
->PostOperation
;
225 else if (Callbacks
->MajorFunction
== IRP_MJ_SHUTDOWN
)
227 Callbacks
->PostOperation
= NULL
;
232 /* Add the filter name buffer onto the end of the data and fill in the string */
233 Filter
->Name
.Length
= 0;
234 Filter
->Name
.MaximumLength
= DriverObject
->DriverExtension
->ServiceKeyName
.Length
;
235 Filter
->Name
.Buffer
= (PWCH
)Ptr
;
236 RtlCopyUnicodeString(&Filter
->Name
, &DriverObject
->DriverExtension
->ServiceKeyName
);
238 Status
= GetFilterAltitude(Filter
, &Filter
->DefaultAltitude
);
239 if (!NT_SUCCESS(Status
))
245 // - Slot the filter into the correct altitude location
249 /* Store any existing driver unload routine before we make any changes */
250 Filter
->OldDriverUnload
= (PFLT_FILTER_UNLOAD_CALLBACK
)DriverObject
->DriverUnload
;
252 /* Check we opted not to have an unload routine, or if we want to stop the driver from being unloaded */
253 if (!FlagOn(Filter
->Flags
, FLTFL_REGISTRATION_DO_NOT_SUPPORT_SERVICE_STOP
))
255 DriverObject
->DriverUnload
= (PDRIVER_UNLOAD
)FltpMiniFilterDriverUnload
;
259 DriverObject
->DriverUnload
= (PDRIVER_UNLOAD
)NULL
;
265 if (NT_SUCCESS(Status
))
267 DPRINT1("Loaded FS mini-filter %wZ\n", &DriverObject
->DriverExtension
->ServiceKeyName
);
272 DPRINT1("Failed to load FS mini-filter %wZ : 0x%X\n", &DriverObject
->DriverExtension
->ServiceKeyName
, Status
);
274 // Add cleanup for context resources
276 ExDeleteResourceLite(&Filter
->InstanceList
.rLock
);
277 ExFreePoolWithTag(Filter
, FM_TAG_FILTER
);
285 FltUnregisterFilter(_In_ PFLT_FILTER Filter
)
287 PFLT_INSTANCE Instance
;
288 PLIST_ENTRY CurrentEntry
;
291 /* Set the draining flag */
292 Status
= FltpStartingToDrainObject(&Filter
->Base
);
293 if (!NT_SUCCESS(Status
))
295 /* Someone already unregistered us, just remove our ref and bail */
296 FltObjectDereference(&Filter
->Base
);
300 /* Lock the instance list */
301 KeEnterCriticalRegion();
302 ExAcquireResourceSharedLite(&Filter
->InstanceList
.rLock
, TRUE
);
304 /* Set the first entry in the list */
305 CurrentEntry
= Filter
->InstanceList
.rList
.Flink
;
307 /* Free all instances referenced by the filter */
308 while (CurrentEntry
!= &Filter
->InstanceList
.rList
)
310 /* Get the record pointer */
311 Instance
= CONTAINING_RECORD(CurrentEntry
, FLT_INSTANCE
, FilterLink
);
316 /* Reset the pointer and move to next entry */
318 CurrentEntry
= CurrentEntry
->Flink
;
321 /* We're done with instances now */
322 ExReleaseResourceLite(&Filter
->InstanceList
.rLock
);
323 KeLeaveCriticalRegion();
325 /* Remove the reference from the base object */
326 FltObjectDereference(&Filter
->Base
);
328 /* Wait until we're sure nothing is using the filter */
329 FltpObjectRundownWait(&Filter
->Base
.RundownRef
);
331 /* Delete the instance list lock */
332 ExDeleteResourceLite(&Filter
->InstanceList
.rLock
);
334 /* We're finished cleaning up now */
335 FltpExRundownCompleted(&Filter
->Base
.RundownRef
);
337 /* Hand the memory back */
338 ExFreePoolWithTag(Filter
, FM_TAG_FILTER
);
343 FltStartFiltering(_In_ PFLT_FILTER Filter
)
347 /* Grab a ref to the filter */
348 Status
= FltObjectReference(&Filter
->Base
);
349 if (NT_SUCCESS(Status
))
351 /* Make sure we aren't already starting up */
352 if (!(Filter
->Flags
& FLTFL_FILTERING_INITIATED
))
358 Status
= STATUS_INVALID_PARAMETER
;
361 FltObjectDereference(&Filter
->Base
);
368 /* INTERNAL FUNCTIONS ******************************************************/
371 FltpStartingToDrainObject(_Inout_ PFLT_OBJECT Object
)
374 * Set the draining flag for the filter. This let's us force
375 * a post op callback for minifilters currently awaiting one.
377 if (InterlockedOr((PLONG
)&Object
->Flags
, FLT_OBFL_DRAINING
) & 1)
379 /* We've been called once, we're already being deleted */
380 return STATUS_FLT_DELETING_OBJECT
;
383 return STATUS_SUCCESS
;
387 FltpMiniFilterDriverUnload()
392 /* PRIVATE FUNCTIONS ******************************************************/
397 _In_ PFLT_FILTER Filter
,
398 _Inout_ PUNICODE_STRING AltitudeString
)
400 UNICODE_STRING InstancesKey
= RTL_CONSTANT_STRING(L
"Instances");
401 UNICODE_STRING DefaultInstance
= RTL_CONSTANT_STRING(L
"DefaultInstance");
402 UNICODE_STRING Altitude
= RTL_CONSTANT_STRING(L
"Altitude");
403 OBJECT_ATTRIBUTES ObjectAttributes
;
404 UNICODE_STRING FilterInstancePath
;
406 HANDLE InstHandle
= NULL
;
408 PWCH InstBuffer
= NULL
;
409 PWCH AltBuffer
= NULL
;
412 /* Get a handle to the instances key in the filter's services key */
413 Status
= FltpOpenFilterServicesKey(Filter
,
417 if (!NT_SUCCESS(Status
))
422 /* Read the size 'default instances' string value */
423 Status
= FltpReadRegistryValue(RootHandle
,
430 /* We should get a buffer too small error */
431 if (Status
== STATUS_BUFFER_TOO_SMALL
)
433 /* Allocate the buffer we need to hold the string */
434 InstBuffer
= ExAllocatePoolWithTag(PagedPool
, BytesRequired
, FM_TAG_UNICODE_STRING
);
435 if (InstBuffer
== NULL
)
437 Status
= STATUS_INSUFFICIENT_RESOURCES
;
441 /* Now read the string value */
442 Status
= FltpReadRegistryValue(RootHandle
,
450 if (!NT_SUCCESS(Status
))
455 /* Convert the string to a unicode_string */
456 RtlInitUnicodeString(&FilterInstancePath
, InstBuffer
);
458 /* Setup the attributes using the root key handle */
459 InitializeObjectAttributes(&ObjectAttributes
,
461 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
465 /* Now open the key name which was stored in the default instance */
466 Status
= ZwOpenKey(&InstHandle
, KEY_QUERY_VALUE
, &ObjectAttributes
);
467 if (NT_SUCCESS(Status
))
469 /* Get the size of the buffer that holds the altitude */
470 Status
= FltpReadRegistryValue(InstHandle
,
476 if (Status
== STATUS_BUFFER_TOO_SMALL
)
478 /* Allocate the required buffer */
479 AltBuffer
= ExAllocatePoolWithTag(PagedPool
, BytesRequired
, FM_TAG_UNICODE_STRING
);
480 if (AltBuffer
== NULL
)
482 Status
= STATUS_INSUFFICIENT_RESOURCES
;
486 /* And now finally read in the actual altitude string */
487 Status
= FltpReadRegistryValue(InstHandle
,
493 if (NT_SUCCESS(Status
))
495 /* We made it, setup the return buffer */
496 AltitudeString
->Length
= BytesRequired
;
497 AltitudeString
->MaximumLength
= BytesRequired
;
498 AltitudeString
->Buffer
= AltBuffer
;
504 if (!NT_SUCCESS(Status
))
508 ExFreePoolWithTag(AltBuffer
, FM_TAG_UNICODE_STRING
);
514 ExFreePoolWithTag(InstBuffer
, FM_TAG_UNICODE_STRING
);
529 FltpReadRegistryValue(
530 _In_ HANDLE KeyHandle
,
531 _In_ PUNICODE_STRING ValueName
,
533 _Out_writes_bytes_(BufferSize
) PVOID Buffer
,
534 _In_ ULONG BufferSize
,
535 _Out_opt_ PULONG BytesRequired