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
FltLoadFilter(_In_ PCUNICODE_STRING FilterName)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
FltUnloadFilter(_In_ PCUNICODE_STRING FilterName)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
FltRegisterFilter(_In_ PDRIVER_OBJECT DriverObject,_In_ const FLT_REGISTRATION * Registration,_Out_ PFLT_FILTER * RetFilter)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
FltUnregisterFilter(_In_ PFLT_FILTER Filter)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
FltStartFiltering(_In_ PFLT_FILTER Filter)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
FltGetFilterFromName(_In_ PCUNICODE_STRING FilterName,_Out_ PFLT_FILTER * RetFilter)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
FltpStartingToDrainObject(_Inout_ PFLT_OBJECT Object)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
FltpMiniFilterDriverUnload()432 FltpMiniFilterDriverUnload()
433 {
434 __debugbreak();
435 }
436
437
438 NTSTATUS
FltpAttachFrame(_In_ PUNICODE_STRING Altitude,_Inout_ PFLTP_FRAME * Frame)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
GetFilterAltitude(_In_ PFLT_FILTER Filter,_Inout_ PUNICODE_STRING AltitudeString)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
GetFilterFrame(_In_ PFLT_FILTER Filter,_In_ PUNICODE_STRING Altitude,_Out_ PFLTP_FRAME * Frame)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