[FLTMGR]
[reactos.git] / reactos / drivers / filters / fltmgr / Filter.c
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
14 #define NDEBUG
15 #include <debug.h>
16
17
18 /* DATA *********************************************************************/
19
20 #define SERVICES_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
21 #define MAX_KEY_LENGTH 0x200
22
23 NTSTATUS
24 FltpStartingToDrainObject(
25 _Inout_ PFLT_OBJECT Object
26 );
27
28
29 /* EXPORTED FUNCTIONS ******************************************************/
30
31 NTSTATUS
32 NTAPI
33 FltLoadFilter(_In_ PCUNICODE_STRING FilterName)
34 {
35 UNICODE_STRING DriverServiceName;
36 UNICODE_STRING ServicesKey;
37 CHAR Buffer[MAX_KEY_LENGTH];
38
39 /* Setup the base services key */
40 RtlInitUnicodeString(&ServicesKey, SERVICES_KEY);
41
42 /* Initialize the string data */
43 DriverServiceName.Length = 0;
44 DriverServiceName.Buffer = (PWCH)Buffer;
45 DriverServiceName.MaximumLength = MAX_KEY_LENGTH;
46
47 /* Create the full service key for this filter */
48 RtlCopyUnicodeString(&DriverServiceName, &ServicesKey);
49 RtlAppendUnicodeStringToString(&DriverServiceName, FilterName);
50
51 /* Ask the kernel to load it for us */
52 return ZwLoadDriver(&DriverServiceName);
53 }
54
55 NTSTATUS
56 NTAPI
57 FltUnloadFilter(_In_ PCUNICODE_STRING FilterName)
58 {
59 UNREFERENCED_PARAMETER(FilterName);
60 return STATUS_NOT_IMPLEMENTED;
61 }
62
63 NTSTATUS
64 NTAPI
65 FltRegisterFilter(_In_ PDRIVER_OBJECT DriverObject,
66 _In_ const FLT_REGISTRATION *Registration,
67 _Out_ PFLT_FILTER *RetFilter)
68 {
69 PFLT_OPERATION_REGISTRATION Callbacks;
70 PFLT_FILTER Filter;
71 ULONG CallbackBufferSize;
72 ULONG FilterBufferSize;
73 ULONG Count = 0;
74 PCHAR Ptr;
75 NTSTATUS Status;
76
77 /* Make sure we're targeting the correct major revision */
78 if ((Registration->Version & 0xFF00) != FLT_MAJOR_VERSION)
79 {
80 return STATUS_INVALID_PARAMETER;
81 }
82
83 /* Make sure our namespace callbacks are valid */
84 if ((!Registration->GenerateFileNameCallback && Registration->NormalizeNameComponentCallback) ||
85 (!Registration->NormalizeNameComponentCallback && Registration->NormalizeContextCleanupCallback))
86 {
87 return STATUS_INVALID_PARAMETER;
88 }
89
90 /* Count the number of operations that were requested */
91 Callbacks = (PFLT_OPERATION_REGISTRATION)Registration->OperationRegistration;
92 while (Callbacks)
93 {
94 Count++;
95
96 /* Bail when we find the last one */
97 if (Callbacks->MajorFunction == IRP_MJ_OPERATION_END)
98 break;
99
100 /* Move to the next item */
101 Callbacks++;
102 }
103
104 /* Calculate the buffer sizes */
105 CallbackBufferSize = Count * sizeof(FLT_OPERATION_REGISTRATION);
106 FilterBufferSize = sizeof(FLT_FILTER) +
107 CallbackBufferSize +
108 DriverObject->DriverExtension->ServiceKeyName.Length;
109
110 /* Allocate a buffer to hold our filter data */
111 Filter = ExAllocatePoolWithTag(NonPagedPool,
112 FilterBufferSize,
113 FM_TAG_FILTER);
114 if (Filter == NULL) return STATUS_INSUFFICIENT_RESOURCES;
115 RtlZeroMemory(Filter, FilterBufferSize);
116
117 /* Find the end of the fixed struct */
118 Ptr = (PCHAR)(Filter + 1);
119
120 /* Store a copy of the driver object of this filter */
121 Filter->DriverObject = DriverObject;
122
123 /* Initialize the base object data */
124 Filter->Base.Flags = FLT_OBFL_TYPE_FILTER;
125 Filter->Base.PointerCount = 1;
126 FltpExInitializeRundownProtection(&Filter->Base.RundownRef);
127 FltObjectReference(&Filter->Base);
128
129 /* Set the callback addresses */
130 Filter->FilterUnload = Registration->FilterUnloadCallback;
131 Filter->InstanceSetup = Registration->InstanceSetupCallback;
132 Filter->InstanceQueryTeardown = Registration->InstanceQueryTeardownCallback;
133 Filter->InstanceTeardownStart = Registration->InstanceTeardownStartCallback;
134 Filter->InstanceTeardownComplete = Registration->InstanceTeardownCompleteCallback;
135 Filter->GenerateFileName = Registration->GenerateFileNameCallback;
136 Filter->NormalizeNameComponent = Registration->NormalizeNameComponentCallback;
137 Filter->NormalizeContextCleanup = Registration->NormalizeContextCleanupCallback;
138
139 /* Initialize the instance list */
140 ExInitializeResourceLite(&Filter->InstanceList.rLock);
141 InitializeListHead(&Filter->InstanceList.rList);
142 Filter->InstanceList.rCount = 0;
143
144 ExInitializeFastMutex(&Filter->ActiveOpens.mLock);
145 InitializeListHead(&Filter->ActiveOpens.mList);
146 Filter->ActiveOpens.mCount = 0;
147
148 /* Initialize the usermode port list */
149 ExInitializeFastMutex(&Filter->PortList.mLock);
150 InitializeListHead(&Filter->PortList.mList);
151 Filter->PortList.mCount = 0;
152
153 /* We got this far, assume success from here */
154 Status = STATUS_SUCCESS;
155
156 /* Check if the caller requested any context data */
157 if (Registration->ContextRegistration)
158 {
159 /* Register the contexts for this filter */
160 Status = FltpRegisterContexts(Filter, Registration->ContextRegistration);
161 if (NT_SUCCESS(Status))
162 {
163 goto Quit;
164 }
165 }
166
167 /* Check if the caller is registering any callbacks */
168 if (Registration->OperationRegistration)
169 {
170 /* The callback data comes after the fixed struct */
171 Filter->Operations = (PFLT_OPERATION_REGISTRATION)Ptr;
172 Ptr += (Count * sizeof(FLT_OPERATION_REGISTRATION));
173
174 /* Tag the operation data onto the end of the filter data */
175 RtlCopyMemory(Filter->Operations, Registration->OperationRegistration, CallbackBufferSize);
176
177 /* walk through the requested callbacks */
178 for (Callbacks = Filter->Operations;
179 Callbacks->MajorFunction != IRP_MJ_OPERATION_END;
180 Callbacks++)
181 {
182 // http://fsfilters.blogspot.co.uk/2011/03/how-file-system-filters-attach-to_17.html
183 /* Check if this is an attach to a volume */
184 if (Callbacks->MajorFunction == IRP_MJ_VOLUME_MOUNT)
185 {
186 Filter->PreVolumeMount = Callbacks->PreOperation;
187 Filter->PostVolumeMount = Callbacks->PostOperation;
188 }
189 else if (Callbacks->MajorFunction == IRP_MJ_SHUTDOWN)
190 {
191 Callbacks->PostOperation = NULL;
192 }
193 }
194 }
195
196 /* Add the filter name buffer onto the end of the data and fill in the string */
197 Filter->Name.Length = 0;
198 Filter->Name.MaximumLength = DriverObject->DriverExtension->ServiceKeyName.Length;
199 Filter->Name.Buffer = (PWCH)Ptr;
200 RtlCopyUnicodeString(&Filter->Name, &DriverObject->DriverExtension->ServiceKeyName);
201
202 //
203 // - Get the altitude string
204 // - Slot the filter into the correct altitude location
205 // - More stuff??
206 //
207
208 Quit:
209 if (!NT_SUCCESS(Status))
210 {
211 // Add cleanup for context resources
212
213 ExDeleteResourceLite(&Filter->InstanceList.rLock);
214 ExFreePoolWithTag(Filter, FM_TAG_FILTER);
215 }
216
217 return Status;
218 }
219
220 VOID
221 FLTAPI
222 FltUnregisterFilter(_In_ PFLT_FILTER Filter)
223 {
224 PFLT_INSTANCE Instance;
225 PLIST_ENTRY CurrentEntry;
226 NTSTATUS Status;
227
228 /* Set the draining flag */
229 Status = FltpStartingToDrainObject(&Filter->Base);
230 if (!NT_SUCCESS(Status))
231 {
232 /* Someone already unregistered us, just remove our ref and bail */
233 FltObjectDereference(&Filter->Base);
234 return;
235 }
236
237 /* Lock the instance list */
238 KeEnterCriticalRegion();
239 ExAcquireResourceSharedLite(&Filter->InstanceList.rLock, TRUE);
240
241 /* Set the first entry in the list */
242 CurrentEntry = Filter->InstanceList.rList.Flink;
243
244 /* Free all instances referenced by the filter */
245 while (CurrentEntry != &Filter->InstanceList.rList)
246 {
247 /* Get the record pointer */
248 Instance = CONTAINING_RECORD(CurrentEntry, FLT_INSTANCE, FilterLink);
249
250 // FIXME: implement
251 (void)Instance;
252
253 /* Reset the pointer and move to next entry */
254 Instance = NULL;
255 CurrentEntry = CurrentEntry->Flink;
256 }
257
258 /* We're done with instances now */
259 ExReleaseResourceLite(&Filter->InstanceList.rLock);
260 KeLeaveCriticalRegion();
261
262 /* Remove the reference from the base object */
263 FltObjectDereference(&Filter->Base);
264
265 /* Wait until we're sure nothing is using the filter */
266 FltpObjectRundownWait(&Filter->Base.RundownRef);
267
268 /* Delete the instance list lock */
269 ExDeleteResourceLite(&Filter->InstanceList.rLock);
270
271 /* We're finished cleaning up now */
272 FltpExRundownCompleted(&Filter->Base.RundownRef);
273
274 /* Hand the memory back */
275 ExFreePoolWithTag(Filter, FM_TAG_FILTER);
276 }
277
278 NTSTATUS
279 NTAPI
280 FltStartFiltering(_In_ PFLT_FILTER Filter)
281 {
282 NTSTATUS Status;
283
284 /* Grab a ref to the filter */
285 Status = FltObjectReference(&Filter->Base);
286 if (NT_SUCCESS(Status))
287 {
288 /* Make sure we aren't already starting up */
289 if (!(Filter->Flags & FLTFL_FILTERING_INITIATED))
290 {
291 // Startup
292 }
293 else
294 {
295 Status = STATUS_INVALID_PARAMETER;
296 }
297
298 FltObjectDereference(&Filter->Base);
299 }
300
301 return Status;
302 }
303
304
305 /* INTERNAL FUNCTIONS ******************************************************/
306
307 NTSTATUS
308 FltpStartingToDrainObject(_Inout_ PFLT_OBJECT Object)
309 {
310 /*
311 * Set the draining flag for the filter. This let's us force
312 * a post op callback for minifilters currently awaiting one.
313 */
314 if (InterlockedOr((PLONG)&Object->Flags, FLT_OBFL_DRAINING) & 1)
315 {
316 /* We've been called once, we're already being deleted */
317 return STATUS_FLT_DELETING_OBJECT;
318 }
319
320 return STATUS_SUCCESS;
321 }