xref: /reactos/drivers/filters/fltmgr/Filter.c (revision 1734f297)
1 /*
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)
7 */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "fltmgr.h"
12 #include "fltmgrint.h"
13 #include "Registry.h"
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 
19 /* DATA *********************************************************************/
20 
21 #define SERVICES_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
22 #define MAX_KEY_LENGTH  0x200
23 
24 LIST_ENTRY FilterList;
25 ERESOURCE FilterListLock;
26 
27 NTSTATUS
28 FltpStartingToDrainObject(
29     _Inout_ PFLT_OBJECT Object
30 );
31 
32 VOID
33 FltpMiniFilterDriverUnload(
34 );
35 
36 NTSTATUS
37 FltpAttachFrame(
38     _In_ PUNICODE_STRING Altitude,
39     _Inout_ PFLTP_FRAME *Frame
40 );
41 
42 static
43 NTSTATUS
44 GetFilterAltitude(
45     _In_ PFLT_FILTER Filter,
46     _Inout_ PUNICODE_STRING AltitudeString
47 );
48 
49 static
50 NTSTATUS
51 GetFilterFrame(
52     _In_ PFLT_FILTER Filter,
53     _In_ PUNICODE_STRING Altitude,
54     _Out_ PFLTP_FRAME *Frame
55 );
56 
57 
58 /* EXPORTED FUNCTIONS ******************************************************/
59 
60 NTSTATUS
61 NTAPI
62 FltLoadFilter(_In_ PCUNICODE_STRING FilterName)
63 {
64     UNICODE_STRING DriverServiceName;
65     UNICODE_STRING ServicesKey;
66     CHAR Buffer[MAX_KEY_LENGTH];
67 
68     /* Setup the base services key */
69     RtlInitUnicodeString(&ServicesKey, SERVICES_KEY);
70 
71     /* Initialize the string data */
72     DriverServiceName.Length = 0;
73     DriverServiceName.Buffer = (PWCH)Buffer;
74     DriverServiceName.MaximumLength = MAX_KEY_LENGTH;
75 
76     /* Create the full service key for this filter */
77     RtlCopyUnicodeString(&DriverServiceName, &ServicesKey);
78     RtlAppendUnicodeStringToString(&DriverServiceName, FilterName);
79 
80     /* Ask the kernel to load it for us */
81     return ZwLoadDriver(&DriverServiceName);
82 }
83 
84 NTSTATUS
85 NTAPI
86 FltUnloadFilter(_In_ PCUNICODE_STRING FilterName)
87 {
88     //
89     //FIXME: This is a temp hack, it needs properly implementing
90     //
91 
92     UNICODE_STRING DriverServiceName;
93     UNICODE_STRING ServicesKey;
94     CHAR Buffer[MAX_KEY_LENGTH];
95 
96     /* Setup the base services key */
97     RtlInitUnicodeString(&ServicesKey, SERVICES_KEY);
98 
99     /* Initialize the string data */
100     DriverServiceName.Length = 0;
101     DriverServiceName.Buffer = (PWCH)Buffer;
102     DriverServiceName.MaximumLength = MAX_KEY_LENGTH;
103 
104     /* Create the full service key for this filter */
105     RtlCopyUnicodeString(&DriverServiceName, &ServicesKey);
106     RtlAppendUnicodeStringToString(&DriverServiceName, FilterName);
107     return ZwUnloadDriver(&DriverServiceName);
108 }
109 
110 NTSTATUS
111 NTAPI
112 FltRegisterFilter(_In_ PDRIVER_OBJECT DriverObject,
113                   _In_ const FLT_REGISTRATION *Registration,
114                   _Out_ PFLT_FILTER *RetFilter)
115 {
116     PFLT_OPERATION_REGISTRATION Callbacks;
117     PFLT_FILTER Filter;
118     PFLTP_FRAME Frame;
119     ULONG CallbackBufferSize;
120     ULONG FilterBufferSize;
121     ULONG Count = 0;
122     PCHAR Ptr;
123     NTSTATUS Status;
124 
125     *RetFilter = NULL;
126 
127     /* Make sure we're targeting the correct major revision */
128     if ((Registration->Version & 0xFF00) != FLT_MAJOR_VERSION)
129     {
130         return STATUS_INVALID_PARAMETER;
131     }
132 
133     /* Make sure our namespace callbacks are valid */
134     if ((!Registration->GenerateFileNameCallback && Registration->NormalizeNameComponentCallback) ||
135         (!Registration->NormalizeNameComponentCallback && Registration->NormalizeContextCleanupCallback))
136     {
137         return STATUS_INVALID_PARAMETER;
138     }
139 
140     /* Count the number of operations that were requested */
141     Callbacks = (PFLT_OPERATION_REGISTRATION)Registration->OperationRegistration;
142     while (Callbacks)
143     {
144         Count++;
145 
146         /* Bail when we find the last one */
147         if (Callbacks->MajorFunction == IRP_MJ_OPERATION_END)
148             break;
149 
150         /* Move to the next item */
151         Callbacks++;
152     }
153 
154     /* Calculate the buffer sizes */
155     CallbackBufferSize = Count * sizeof(FLT_OPERATION_REGISTRATION);
156     FilterBufferSize = sizeof(FLT_FILTER) +
157                        CallbackBufferSize +
158                        DriverObject->DriverExtension->ServiceKeyName.Length;
159 
160     /* Allocate a buffer to hold our filter data */
161     Filter = ExAllocatePoolWithTag(NonPagedPool,
162                                    FilterBufferSize,
163                                    FM_TAG_FILTER);
164     if (Filter == NULL) return STATUS_INSUFFICIENT_RESOURCES;
165     RtlZeroMemory(Filter, FilterBufferSize);
166 
167     /* Find the end of the fixed struct */
168     Ptr = (PCHAR)(Filter + 1);
169 
170     /* Store a copy of the driver object of this filter */
171     Filter->DriverObject = DriverObject;
172 
173     /* Initialize the base object data */
174     Filter->Base.Flags = FLT_OBFL_TYPE_FILTER;
175     Filter->Base.PointerCount = 1;
176     FltpExInitializeRundownProtection(&Filter->Base.RundownRef);
177     FltObjectReference(&Filter->Base);
178 
179     /* Set the callback addresses */
180     Filter->FilterUnload = Registration->FilterUnloadCallback;
181     Filter->InstanceSetup = Registration->InstanceSetupCallback;
182     Filter->InstanceQueryTeardown = Registration->InstanceQueryTeardownCallback;
183     Filter->InstanceTeardownStart = Registration->InstanceTeardownStartCallback;
184     Filter->InstanceTeardownComplete = Registration->InstanceTeardownCompleteCallback;
185     Filter->GenerateFileName = Registration->GenerateFileNameCallback;
186     Filter->NormalizeNameComponent = Registration->NormalizeNameComponentCallback;
187     Filter->NormalizeContextCleanup = Registration->NormalizeContextCleanupCallback;
188 
189     /* Initialize the instance list */
190     ExInitializeResourceLite(&Filter->InstanceList.rLock);
191     InitializeListHead(&Filter->InstanceList.rList);
192     Filter->InstanceList.rCount = 0;
193 
194     ExInitializeFastMutex(&Filter->ActiveOpens.mLock);
195     InitializeListHead(&Filter->ActiveOpens.mList);
196     Filter->ActiveOpens.mCount = 0;
197 
198     ExInitializeFastMutex(&Filter->ConnectionList.mLock);
199     InitializeListHead(&Filter->ConnectionList.mList);
200     Filter->ConnectionList.mCount = 0;
201 
202     /* Initialize the usermode port list */
203     ExInitializeFastMutex(&Filter->PortList.mLock);
204     InitializeListHead(&Filter->PortList.mList);
205     Filter->PortList.mCount = 0;
206 
207     /* We got this far, assume success from here */
208     Status = STATUS_SUCCESS;
209 
210     /* Check if the caller requested any context data */
211     if (Registration->ContextRegistration)
212     {
213         /* Register the contexts for this filter */
214         Status = FltpRegisterContexts(Filter, Registration->ContextRegistration);
215         if (NT_SUCCESS(Status))
216         {
217             goto Quit;
218         }
219     }
220 
221     /* Check if the caller is registering any callbacks */
222     if (Registration->OperationRegistration)
223     {
224         /* The callback data comes after the fixed struct */
225         Filter->Operations = (PFLT_OPERATION_REGISTRATION)Ptr;
226         Ptr += (Count * sizeof(FLT_OPERATION_REGISTRATION));
227 
228         /* Tag the operation data onto the end of the filter data */
229         RtlCopyMemory(Filter->Operations, Registration->OperationRegistration, CallbackBufferSize);
230 
231         /* walk through the requested callbacks */
232         for (Callbacks = Filter->Operations;
233              Callbacks->MajorFunction != IRP_MJ_OPERATION_END;
234              Callbacks++)
235         {
236             // http://fsfilters.blogspot.co.uk/2011/03/how-file-system-filters-attach-to_17.html
237             /* Check if this is an attach to a volume */
238             if (Callbacks->MajorFunction == IRP_MJ_VOLUME_MOUNT)
239             {
240                 Filter->PreVolumeMount = Callbacks->PreOperation;
241                 Filter->PostVolumeMount = Callbacks->PostOperation;
242             }
243             else if (Callbacks->MajorFunction == IRP_MJ_SHUTDOWN)
244             {
245                 Callbacks->PostOperation = NULL;
246             }
247         }
248     }
249 
250     /* Add the filter name buffer onto the end of the data and fill in the string */
251     Filter->Name.Length = 0;
252     Filter->Name.MaximumLength = DriverObject->DriverExtension->ServiceKeyName.Length;
253     Filter->Name.Buffer = (PWCH)Ptr;
254     RtlCopyUnicodeString(&Filter->Name, &DriverObject->DriverExtension->ServiceKeyName);
255 
256     /* Lookup the altitude of the mini-filter */
257     Status = GetFilterAltitude(Filter, &Filter->DefaultAltitude);
258     if (!NT_SUCCESS(Status))
259     {
260         goto Quit;
261     }
262 
263     /* Lookup the filter frame */
264     Status = GetFilterFrame(Filter, &Filter->DefaultAltitude, &Frame);
265     if (Status == STATUS_NOT_FOUND)
266     {
267         /* Store the frame this mini-filter's main struct */
268         Filter->Frame = Frame;
269 
270         Status = FltpAttachFrame(&Filter->DefaultAltitude, &Frame);
271     }
272 
273     if (!NT_SUCCESS(Status))
274     {
275         goto Quit;
276     }
277 
278     //
279     // - Slot the filter into the correct altitude location
280     // - More stuff??
281     //
282 
283     /* Store any existing driver unload routine before we make any changes */
284     Filter->OldDriverUnload = (PFLT_FILTER_UNLOAD_CALLBACK)DriverObject->DriverUnload;
285 
286     /* Check we opted not to have an unload routine, or if we want to stop the driver from being unloaded */
287     if (Registration->FilterUnloadCallback && !FlagOn(Filter->Flags, FLTFL_REGISTRATION_DO_NOT_SUPPORT_SERVICE_STOP))
288     {
289         DriverObject->DriverUnload = (PDRIVER_UNLOAD)FltpMiniFilterDriverUnload;
290     }
291     else
292     {
293         DriverObject->DriverUnload = (PDRIVER_UNLOAD)NULL;
294     }
295 
296 
297 Quit:
298 
299     if (NT_SUCCESS(Status))
300     {
301         DPRINT1("Loaded FS mini-filter %wZ\n", &DriverObject->DriverExtension->ServiceKeyName);
302         *RetFilter = Filter;
303     }
304     else
305     {
306         DPRINT1("Failed to load FS mini-filter %wZ : 0x%X\n", &DriverObject->DriverExtension->ServiceKeyName, Status);
307 
308         // Add cleanup for context resources
309 
310         ExDeleteResourceLite(&Filter->InstanceList.rLock);
311         ExFreePoolWithTag(Filter, FM_TAG_FILTER);
312     }
313 
314     return Status;
315 }
316 
317 VOID
318 FLTAPI
319 FltUnregisterFilter(_In_ PFLT_FILTER Filter)
320 {
321     PFLT_INSTANCE Instance;
322     PLIST_ENTRY CurrentEntry;
323     NTSTATUS Status;
324 
325     /* Set the draining flag */
326     Status = FltpStartingToDrainObject(&Filter->Base);
327     if (!NT_SUCCESS(Status))
328     {
329         /* Someone already unregistered us, just remove our ref and bail */
330         FltObjectDereference(&Filter->Base);
331         return;
332     }
333 
334     /* Lock the instance list */
335     KeEnterCriticalRegion();
336     ExAcquireResourceSharedLite(&Filter->InstanceList.rLock, TRUE);
337 
338     /* Set the first entry in the list */
339     CurrentEntry = Filter->InstanceList.rList.Flink;
340 
341     /* Free all instances referenced by the filter */
342     while (CurrentEntry != &Filter->InstanceList.rList)
343     {
344         /* Get the record pointer */
345         Instance = CONTAINING_RECORD(CurrentEntry, FLT_INSTANCE, FilterLink);
346 
347         // FIXME: implement
348         (void)Instance;
349 
350         /* Reset the pointer and move to next entry */
351         Instance = NULL;
352         CurrentEntry = CurrentEntry->Flink;
353     }
354 
355     /* We're done with instances now */
356     ExReleaseResourceLite(&Filter->InstanceList.rLock);
357     KeLeaveCriticalRegion();
358 
359     /* Remove the reference from the base object */
360     FltObjectDereference(&Filter->Base);
361 
362     /* Wait until we're sure nothing is using the filter */
363     FltpObjectRundownWait(&Filter->Base.RundownRef);
364 
365     /* Delete the instance list lock */
366     ExDeleteResourceLite(&Filter->InstanceList.rLock);
367 
368     /* We're finished cleaning up now */
369     FltpExRundownCompleted(&Filter->Base.RundownRef);
370 
371     /* Hand the memory back */
372     ExFreePoolWithTag(Filter, FM_TAG_FILTER);
373 }
374 
375 NTSTATUS
376 NTAPI
377 FltStartFiltering(_In_ PFLT_FILTER Filter)
378 {
379     NTSTATUS Status;
380 
381     /* Grab a ref to the filter */
382     Status = FltObjectReference(&Filter->Base);
383     if (NT_SUCCESS(Status))
384     {
385         /* Make sure we aren't already starting up */
386         if (!(Filter->Flags & FLTFL_FILTERING_INITIATED))
387         {
388             // Startup
389         }
390         else
391         {
392             Status = STATUS_INVALID_PARAMETER;
393         }
394 
395         FltObjectDereference(&Filter->Base);
396     }
397 
398     return Status;
399 }
400 
401 NTSTATUS
402 NTAPI
403 FltGetFilterFromName(_In_ PCUNICODE_STRING FilterName,
404                      _Out_ PFLT_FILTER *RetFilter)
405 {
406    UNIMPLEMENTED;
407     UNREFERENCED_PARAMETER(FilterName);
408     *RetFilter = NULL;
409     return STATUS_NOT_IMPLEMENTED;
410 }
411 
412 
413 /* INTERNAL FUNCTIONS ******************************************************/
414 
415 NTSTATUS
416 FltpStartingToDrainObject(_Inout_ PFLT_OBJECT Object)
417 {
418     /*
419      * Set the draining flag for the filter. This let's us force
420      * a post op callback for minifilters currently awaiting one.
421      */
422     if (InterlockedOr((PLONG)&Object->Flags, FLT_OBFL_DRAINING) & 1)
423     {
424         /* We've been called once, we're already being deleted */
425         return STATUS_FLT_DELETING_OBJECT;
426     }
427 
428     return STATUS_SUCCESS;
429 }
430 
431 VOID
432 FltpMiniFilterDriverUnload()
433 {
434     __debugbreak();
435 }
436 
437 
438 NTSTATUS
439 FltpAttachFrame(
440     _In_ PUNICODE_STRING Altitude,
441     _Inout_ PFLTP_FRAME *Frame)
442 {
443     UNIMPLEMENTED;
444     UNREFERENCED_PARAMETER(Altitude);
445     *Frame = NULL;
446     return STATUS_SUCCESS;
447 }
448 
449 /* PRIVATE FUNCTIONS ******************************************************/
450 
451 static
452 NTSTATUS
453 GetFilterAltitude(_In_ PFLT_FILTER Filter,
454                   _Inout_ PUNICODE_STRING AltitudeString)
455 {
456     UNICODE_STRING InstancesKey = RTL_CONSTANT_STRING(L"Instances");
457     UNICODE_STRING DefaultInstance = RTL_CONSTANT_STRING(L"DefaultInstance");
458     UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"Altitude");
459     OBJECT_ATTRIBUTES ObjectAttributes;
460     UNICODE_STRING FilterInstancePath;
461     ULONG BytesRequired;
462     HANDLE InstHandle = NULL;
463     HANDLE RootHandle;
464     PWCH InstBuffer = NULL;
465     PWCH AltBuffer = NULL;
466     NTSTATUS Status;
467 
468     /* Get a handle to the instances key in the filter's services key */
469     Status = FltpOpenFilterServicesKey(Filter,
470                                        KEY_QUERY_VALUE,
471                                        &InstancesKey,
472                                        &RootHandle);
473     if (!NT_SUCCESS(Status))
474     {
475         return Status;
476     }
477 
478     /* Read the size 'default instances' string value */
479     Status = FltpReadRegistryValue(RootHandle,
480                                    &DefaultInstance,
481                                    REG_SZ,
482                                    NULL,
483                                    0,
484                                    &BytesRequired);
485 
486     /* We should get a buffer too small error */
487     if (Status == STATUS_BUFFER_TOO_SMALL)
488     {
489         /* Allocate the buffer we need to hold the string */
490         InstBuffer = ExAllocatePoolWithTag(PagedPool, BytesRequired, FM_TAG_UNICODE_STRING);
491         if (InstBuffer == NULL)
492         {
493             Status = STATUS_INSUFFICIENT_RESOURCES;
494             goto Quit;
495         }
496 
497         /* Now read the string value */
498         Status = FltpReadRegistryValue(RootHandle,
499                                        &DefaultInstance,
500                                        REG_SZ,
501                                        InstBuffer,
502                                        BytesRequired,
503                                        &BytesRequired);
504     }
505 
506     if (!NT_SUCCESS(Status))
507     {
508         goto Quit;
509     }
510 
511     /* Convert the string to a unicode_string */
512     RtlInitUnicodeString(&FilterInstancePath, InstBuffer);
513 
514     /* Setup the attributes using the root key handle */
515     InitializeObjectAttributes(&ObjectAttributes,
516                                &FilterInstancePath,
517                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
518                                RootHandle,
519                                NULL);
520 
521     /* Now open the key name which was stored in the default instance */
522     Status = ZwOpenKey(&InstHandle, KEY_QUERY_VALUE, &ObjectAttributes);
523     if (NT_SUCCESS(Status))
524     {
525         /* Get the size of the buffer that holds the altitude */
526         Status = FltpReadRegistryValue(InstHandle,
527                                        &Altitude,
528                                        REG_SZ,
529                                        NULL,
530                                        0,
531                                        &BytesRequired);
532         if (Status == STATUS_BUFFER_TOO_SMALL)
533         {
534             /* Allocate the required buffer */
535             AltBuffer = ExAllocatePoolWithTag(PagedPool, BytesRequired, FM_TAG_UNICODE_STRING);
536             if (AltBuffer == NULL)
537             {
538                 Status = STATUS_INSUFFICIENT_RESOURCES;
539                 goto Quit;
540             }
541 
542             /* And now finally read in the actual altitude string */
543             Status = FltpReadRegistryValue(InstHandle,
544                                            &Altitude,
545                                            REG_SZ,
546                                            AltBuffer,
547                                            BytesRequired,
548                                            &BytesRequired);
549             if (NT_SUCCESS(Status))
550             {
551                 /* We made it, setup the return buffer */
552                 AltitudeString->Length = BytesRequired;
553                 AltitudeString->MaximumLength = BytesRequired;
554                 AltitudeString->Buffer = AltBuffer;
555             }
556         }
557     }
558 
559 Quit:
560     if (!NT_SUCCESS(Status))
561     {
562         if (AltBuffer)
563         {
564             ExFreePoolWithTag(AltBuffer, FM_TAG_UNICODE_STRING);
565         }
566     }
567 
568     if (InstBuffer)
569     {
570         ExFreePoolWithTag(InstBuffer, FM_TAG_UNICODE_STRING);
571     }
572 
573     if (InstHandle)
574     {
575         ZwClose(InstHandle);
576     }
577     ZwClose(RootHandle);
578 
579     return Status;
580 }
581 
582 static
583 NTSTATUS
584 GetFilterFrame(_In_ PFLT_FILTER Filter,
585                _In_ PUNICODE_STRING Altitude,
586                _Out_ PFLTP_FRAME *Frame)
587 {
588     UNIMPLEMENTED;
589     UNREFERENCED_PARAMETER(Filter);
590     UNREFERENCED_PARAMETER(Altitude);
591 
592     //
593     // Try to find a frame from our existing filter list (see FilterList)
594     // If none exists, create a new frame, add it and return it
595     //
596 
597     *Frame = NULL;
598     return STATUS_SUCCESS;
599 }
600