1 /*
2 * PROJECT: ReactOS kernel-mode tests - Filter Manager
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: FS Mini-filter wrapper to host the filter manager tests
5 * COPYRIGHT: Copyright 2017 Thomas Faber <thomas.faber@reactos.org>
6 * Copyright 2017 Ged Murphy <ged.murphy@reactos.org>
7 */
8
9 #include <ntifs.h>
10 #include <ndk/ketypes.h>
11 #include <fltkernel.h>
12
13 #define KMT_DEFINE_TEST_FUNCTIONS
14 #include <kmt_test.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 #include <kmt_public.h>
20
21 #define KMTEST_FILTER_POOL_TAG 'fTMK'
22
23
24 typedef struct _FILTER_DATA
25 {
26 PDRIVER_OBJECT DriverObject;
27 FLT_REGISTRATION FilterRegistration;
28 PFLT_FILTER Filter;
29 PFLT_PORT ServerPort;
30
31 } FILTER_DATA, *PFILTER_DATA;
32
33
34 /* Prototypes */
35 DRIVER_INITIALIZE DriverEntry;
36
37 /* Globals */
38 static PDRIVER_OBJECT TestDriverObject;
39 static PDEVICE_OBJECT KmtestDeviceObject;
40 static FILTER_DATA FilterData;
41 static PFLT_OPERATION_REGISTRATION Callbacks = NULL;
42 static ULONG CallbacksCount = 0;
43 static PFLT_CONTEXT_REGISTRATION Contexts = NULL;
44
45 static PFLT_CONNECT_NOTIFY FilterConnect = NULL;
46 static PFLT_DISCONNECT_NOTIFY FilterDisconnect = NULL;
47 static PFLT_MESSAGE_NOTIFY FilterMessage = NULL;
48 static LONG MaxConnections = 0;
49
50 NTSTATUS
51 FLTAPI
52 FilterUnload(
53 _In_ FLT_FILTER_UNLOAD_FLAGS Flags
54 );
55
56 NTSTATUS
57 FLTAPI
58 FilterInstanceSetup(
59 _In_ PCFLT_RELATED_OBJECTS FltObjects,
60 _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
61 _In_ DEVICE_TYPE VolumeDeviceType,
62 _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
63 );
64
65 NTSTATUS
66 FLTAPI
67 FilterQueryTeardown(
68 _In_ PCFLT_RELATED_OBJECTS FltObjects,
69 _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
70 );
71
72 FLT_PREOP_CALLBACK_STATUS
73 FLTAPI
74 FilterPreOperation(
75 _Inout_ PFLT_CALLBACK_DATA Data,
76 _In_ PCFLT_RELATED_OBJECTS FltObjects,
77 _Outptr_result_maybenull_ PVOID *CompletionContext
78 );
79
80 FLT_POSTOP_CALLBACK_STATUS
81 FLTAPI
82 FilterPostOperation(
83 _Inout_ PFLT_CALLBACK_DATA Data,
84 _In_ PCFLT_RELATED_OBJECTS FltObjects,
85 _In_opt_ PVOID CompletionContext,
86 _In_ FLT_POST_OPERATION_FLAGS Flags
87 );
88
89
90
91 FLT_REGISTRATION FilterRegistration =
92 {
93 sizeof(FLT_REGISTRATION), // Size
94 FLT_REGISTRATION_VERSION, // Version
95 0, // Flags
96 NULL, // ContextRegistration
97 NULL, // OperationRegistration
98 FilterUnload, // FilterUnloadCallback
99 FilterInstanceSetup, // InstanceSetupCallback
100 FilterQueryTeardown, // InstanceQueryTeardownCallback
101 NULL, // InstanceTeardownStartCallback
102 NULL, // InstanceTeardownCompleteCallback
103 NULL, // AmFilterGenerateFileNameCallback
104 NULL, // AmFilterNormalizeNameComponentCallback
105 NULL, // NormalizeContextCleanupCallback
106 #if FLT_MGR_LONGHORN
107 NULL, // TransactionNotificationCallback
108 NULL, // AmFilterNormalizeNameComponentExCallback
109 #endif
110 };
111
112
113
114 /* Filter Interface Routines ****************************/
115
116 /**
117 * @name DriverEntry
118 *
119 * Driver entry point.
120 *
121 * @param DriverObject
122 * Driver Object
123 * @param RegistryPath
124 * Driver Registry Path
125 *
126 * @return Status
127 */
128 NTSTATUS
129 NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)130 DriverEntry(
131 IN PDRIVER_OBJECT DriverObject,
132 IN PUNICODE_STRING RegistryPath)
133 {
134 NTSTATUS Status = STATUS_SUCCESS;
135 OBJECT_ATTRIBUTES ObjectAttributes;
136 PSECURITY_DESCRIPTOR SecurityDescriptor;
137 UNICODE_STRING DeviceName;
138 WCHAR DeviceNameBuffer[128] = L"\\Device\\Kmtest-";
139 UNICODE_STRING KmtestDeviceName;
140 PFILE_OBJECT KmtestFileObject;
141 PKMT_DEVICE_EXTENSION KmtestDeviceExtension;
142 PCWSTR DeviceNameSuffix;
143 INT Flags = 0;
144 PKPRCB Prcb;
145
146 PAGED_CODE();
147 //__debugbreak();
148 DPRINT("DriverEntry\n");
149
150 RtlZeroMemory(&FilterData, sizeof(FILTER_DATA));
151
152 Prcb = KeGetCurrentPrcb();
153 KmtIsCheckedBuild = (Prcb->BuildType & PRCB_BUILD_DEBUG) != 0;
154 KmtIsMultiProcessorBuild = (Prcb->BuildType & PRCB_BUILD_UNIPROCESSOR) == 0;
155 TestDriverObject = DriverObject;
156
157 /* get the Kmtest device, so that we get a ResultBuffer pointer */
158 RtlInitUnicodeString(&KmtestDeviceName, KMTEST_DEVICE_DRIVER_PATH);
159 Status = IoGetDeviceObjectPointer(&KmtestDeviceName, FILE_ALL_ACCESS, &KmtestFileObject, &KmtestDeviceObject);
160
161 if (!NT_SUCCESS(Status))
162 {
163 DPRINT1("Failed to get Kmtest device object pointer\n");
164 goto cleanup;
165 }
166
167 Status = ObReferenceObjectByPointer(KmtestDeviceObject, FILE_ALL_ACCESS, NULL, KernelMode);
168
169 if (!NT_SUCCESS(Status))
170 {
171 DPRINT1("Failed to reference Kmtest device object\n");
172 goto cleanup;
173 }
174
175 ObDereferenceObject(KmtestFileObject);
176 KmtestFileObject = NULL;
177 KmtestDeviceExtension = KmtestDeviceObject->DeviceExtension;
178 ResultBuffer = KmtestDeviceExtension->ResultBuffer;
179 DPRINT("KmtestDeviceObject: %p\n", (PVOID)KmtestDeviceObject);
180 DPRINT("KmtestDeviceExtension: %p\n", (PVOID)KmtestDeviceExtension);
181 DPRINT("Setting ResultBuffer: %p\n", (PVOID)ResultBuffer);
182
183
184 /* call TestEntry */
185 RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
186 DeviceName.MaximumLength = sizeof DeviceNameBuffer;
187 TestEntry(DriverObject, RegistryPath, &DeviceNameSuffix, &Flags);
188
189 RtlAppendUnicodeToString(&DeviceName, DeviceNameSuffix);
190
191 /* Register with the filter manager */
192 if (!(Flags & TESTENTRY_NO_REGISTER_FILTER))
193 {
194 Status = FltRegisterFilter(DriverObject,
195 &FilterRegistration,
196 &FilterData.Filter);
197 if (!NT_SUCCESS(Status))
198 {
199 DPRINT1("Failed to register the filter driver %wZ\n", &DeviceName);
200 goto cleanup;
201 }
202 }
203
204 if (!(Flags & TESTENTRY_NO_CREATE_COMMS_PORT))
205 {
206 /* Create a security descriptor */
207 Status = FltBuildDefaultSecurityDescriptor(&SecurityDescriptor,
208 FLT_PORT_ALL_ACCESS);
209 if (!NT_SUCCESS(Status))
210 {
211 goto cleanup;
212 }
213
214 /* Initialize the security descriptor object */
215 InitializeObjectAttributes(&ObjectAttributes,
216 &DeviceName,
217 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
218 NULL,
219 SecurityDescriptor);
220
221
222 /* Create the usermode communication port */
223 Status = FltCreateCommunicationPort(FilterData.Filter,
224 &FilterData.ServerPort,
225 &ObjectAttributes,
226 NULL,
227 FilterConnect,
228 FilterDisconnect,
229 FilterMessage,
230 MaxConnections);
231
232 /* Free the security descriptor */
233 FltFreeSecurityDescriptor(SecurityDescriptor);
234
235 if (!NT_SUCCESS(Status))
236 {
237 goto cleanup;
238 }
239 }
240
241 if (!(Flags & TESTENTRY_NO_START_FILTERING))
242 {
243 /* Start filtering the requests */
244 Status = FltStartFiltering(FilterData.Filter);
245 }
246
247 cleanup:
248 if (!NT_SUCCESS(Status))
249 {
250 if (FilterData.ServerPort)
251 {
252 FltCloseCommunicationPort(FilterData.ServerPort);
253 }
254 if (FilterData.Filter)
255 {
256 FltUnregisterFilter(FilterData.Filter);
257 }
258 }
259
260 return Status;
261 }
262
263 /**
264 * @name DriverUnload
265 *
266 * Driver cleanup funtion.
267 *
268 * @param Flags
269 * Flags describing the unload request
270 */
271 NTSTATUS
272 FLTAPI
FilterUnload(_In_ FLT_FILTER_UNLOAD_FLAGS Flags)273 FilterUnload(
274 _In_ FLT_FILTER_UNLOAD_FLAGS Flags)
275 {
276 PAGED_CODE();
277 UNREFERENCED_PARAMETER(Flags);
278 //__debugbreak();
279
280 DPRINT("DriverUnload\n");
281
282 TestFilterUnload(Flags);
283
284 /* Close the port and unregister the filter */
285 if (FilterData.ServerPort)
286 {
287 FltCloseCommunicationPort(FilterData.ServerPort);
288 FilterData.ServerPort = NULL;
289 }
290 if (FilterData.Filter)
291 {
292 FltUnregisterFilter(FilterData.Filter);
293 FilterData.Filter = NULL;
294 }
295
296 if (Callbacks)
297 {
298 ExFreePoolWithTag(Callbacks, KMTEST_FILTER_POOL_TAG);
299 Callbacks = NULL;
300 CallbacksCount = 0;
301 }
302
303 return STATUS_SUCCESS;
304 }
305
306
307 /**
308 * @name FilterInstanceSetup
309 *
310 * Volume attach routine
311 *
312 * @param FltObjects
313 * Filter Object Pointers
314 * Pointer to an FLT_RELATED_OBJECTS structure that contains opaque pointers
315 * for the objects related to the current operation
316 * @param Flags
317 * Bitmask of flags that indicate why the instance is being attached
318 * @param VolumeDeviceType
319 * Device type of the file system volume
320 * @param VolumeFilesystemType
321 * File system type of the volume
322 *
323 * @return Status.
324 * Return STATUS_SUCCESS to attach or STATUS_FLT_DO_NOT_ATTACH to ignore
325 */
326 NTSTATUS
327 FLTAPI
FilterInstanceSetup(_In_ PCFLT_RELATED_OBJECTS FltObjects,_In_ FLT_INSTANCE_SETUP_FLAGS Flags,_In_ DEVICE_TYPE VolumeDeviceType,_In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType)328 FilterInstanceSetup(
329 _In_ PCFLT_RELATED_OBJECTS FltObjects,
330 _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
331 _In_ DEVICE_TYPE VolumeDeviceType,
332 _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType)
333 {
334 #if 0
335 UCHAR VolPropBuffer[sizeof(FLT_VOLUME_PROPERTIES) + 512];
336 PFLT_VOLUME_PROPERTIES VolumeProperties = (PFLT_VOLUME_PROPERTIES)VolPropBuffer;
337 #endif
338 PDEVICE_OBJECT DeviceObject = NULL;
339 UNICODE_STRING VolumeName;
340 ULONG ReportedSectorSize = 0;
341 ULONG SectorSize = 0;
342 NTSTATUS Status = STATUS_SUCCESS;
343
344 PAGED_CODE();
345
346 UNREFERENCED_PARAMETER(FltObjects);
347 UNREFERENCED_PARAMETER(Flags);
348
349 if (!(Flags & TESTENTRY_NO_INSTANCE_SETUP))
350 {
351 RtlInitUnicodeString(&VolumeName, NULL);
352
353 #if 0 // FltGetVolumeProperties is not yet implemented
354 /* Get the properties of this volume */
355 Status = FltGetVolumeProperties(Volume,
356 VolumeProperties,
357 sizeof(VolPropBuffer),
358 &LengthReturned);
359 if (NT_SUCCESS(Status))
360 {
361 FLT_ASSERT((VolumeProperties->SectorSize == 0) || (VolumeProperties->SectorSize >= MIN_SECTOR_SIZE));
362 SectorSize = max(VolumeProperties->SectorSize, MIN_SECTOR_SIZE);
363 ReportedSectorSize = VolumeProperties->SectorSize;
364 }
365 else
366 {
367 DPRINT1("Failed to get the volume properties : 0x%X", Status);
368 return Status;
369 }
370 #endif
371 /* Get the storage device object we want a name for */
372 Status = FltGetDiskDeviceObject(FltObjects->Volume, &DeviceObject);
373 if (NT_SUCCESS(Status))
374 {
375 /* Get the dos device name */
376 Status = IoVolumeDeviceToDosName(DeviceObject, &VolumeName);
377 if (NT_SUCCESS(Status))
378 {
379 DPRINT("VolumeDeviceType %lu, VolumeFilesystemType %lu, Real SectSize=0x%04x, Reported SectSize=0x%04x, Name=\"%wZ\"",
380 VolumeDeviceType,
381 VolumeFilesystemType,
382 SectorSize,
383 ReportedSectorSize,
384 &VolumeName);
385
386 Status = TestInstanceSetup(FltObjects,
387 Flags,
388 VolumeDeviceType,
389 VolumeFilesystemType,
390 &VolumeName,
391 SectorSize,
392 ReportedSectorSize);
393
394 /* The buffer was allocated by the IoMgr */
395 ExFreePool(VolumeName.Buffer);
396 }
397 }
398 }
399
400 return Status;
401 }
402
403
404 /**
405 * @name FilterQueryTeardown
406 *
407 * Volume detatch routine
408 *
409 * @param FltObjects
410 * Filter Object Pointers
411 * Pointer to an FLT_RELATED_OBJECTS structure that contains opaque pointers
412 * for the objects related to the current operation
413 * @param Flags
414 * Flag that indicates why the minifilter driver instance is being torn down
415 *
416 */
417 NTSTATUS
418 FLTAPI
FilterQueryTeardown(_In_ PCFLT_RELATED_OBJECTS FltObjects,_In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)419 FilterQueryTeardown(
420 _In_ PCFLT_RELATED_OBJECTS FltObjects,
421 _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
422 {
423 PAGED_CODE();
424
425 if (!(Flags & TESTENTRY_NO_QUERY_TEARDOWN))
426 {
427 TestQueryTeardown(FltObjects, Flags);
428 }
429
430 /* We always allow a volume to detach */
431 return STATUS_SUCCESS;
432 }
433
434
435 /* Public Routines **************************************/
436
437 NTSTATUS
KmtFilterRegisterCallbacks(_In_ CONST FLT_OPERATION_REGISTRATION * OperationRegistration)438 KmtFilterRegisterCallbacks(
439 _In_ CONST FLT_OPERATION_REGISTRATION *OperationRegistration)
440 {
441 ULONG Count = 0;
442 ULONG i;
443
444 if (Callbacks)
445 {
446 return STATUS_ALREADY_REGISTERED;
447 }
448
449 /* Count how many IRPs being registered */
450 for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
451 {
452 if (OperationRegistration[i].MajorFunction == IRP_MJ_OPERATION_END)
453 break;
454 Count++;
455 }
456
457 /* Allocate enough pool to hold a copy of the array */
458 Callbacks = ExAllocatePoolWithTag(NonPagedPool,
459 sizeof(FLT_OPERATION_REGISTRATION) * (Count + 1),
460 KMTEST_FILTER_POOL_TAG);
461 if (Callbacks == NULL)
462 {
463 return STATUS_INSUFFICIENT_RESOURCES;
464 }
465
466 /* Copy the array, but using our own pre/post callbacks */
467 for (i = 0; i < Count; i++)
468 {
469 Callbacks[i].MajorFunction = OperationRegistration[i].MajorFunction;
470 Callbacks[i].Flags = OperationRegistration[i].Flags;
471 Callbacks[i].PreOperation = FilterPreOperation;
472 Callbacks[i].PostOperation = FilterPostOperation;
473 }
474
475 /* Terminate the array */
476 Callbacks[Count].MajorFunction = IRP_MJ_OPERATION_END;
477
478 /* Save the count of IRPs, not counting IRP_MJ_OPERATION_END last entry */
479 CallbacksCount = Count;
480
481 return STATUS_SUCCESS;
482 }
483
484 NTSTATUS
KmtFilterRegisterContexts(_In_ PFLT_CONTEXT_REGISTRATION ContextRegistration,_In_ PVOID Callback)485 KmtFilterRegisterContexts(
486 _In_ PFLT_CONTEXT_REGISTRATION ContextRegistration,
487 _In_ PVOID Callback)
488 {
489 UNREFERENCED_PARAMETER(ContextRegistration);
490 UNREFERENCED_PARAMETER(Callback);
491 UNREFERENCED_PARAMETER(Contexts);
492 return STATUS_NOT_IMPLEMENTED;
493 }
494
495 NTSTATUS
KmtFilterRegisterComms(_In_ PFLT_CONNECT_NOTIFY ConnectNotifyCallback,_In_ PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,_In_opt_ PFLT_MESSAGE_NOTIFY MessageNotifyCallback,_In_ LONG MaxClientConnections)496 KmtFilterRegisterComms(
497 _In_ PFLT_CONNECT_NOTIFY ConnectNotifyCallback,
498 _In_ PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,
499 _In_opt_ PFLT_MESSAGE_NOTIFY MessageNotifyCallback,
500 _In_ LONG MaxClientConnections)
501 {
502 FilterConnect = ConnectNotifyCallback;
503 FilterDisconnect = DisconnectNotifyCallback;
504 FilterMessage = MessageNotifyCallback;
505 MaxConnections = MaxClientConnections;
506
507 return STATUS_SUCCESS;
508 }
509
510
511 /* Private Routines ******************************************/
512
513 FLT_PREOP_CALLBACK_STATUS
514 FLTAPI
FilterPreOperation(_Inout_ PFLT_CALLBACK_DATA Data,_In_ PCFLT_RELATED_OBJECTS FltObjects,_Outptr_result_maybenull_ PVOID * CompletionContext)515 FilterPreOperation(
516 _Inout_ PFLT_CALLBACK_DATA Data,
517 _In_ PCFLT_RELATED_OBJECTS FltObjects,
518 _Outptr_result_maybenull_ PVOID *CompletionContext)
519 {
520 FLT_PREOP_CALLBACK_STATUS Status;
521 UCHAR MajorFunction;
522 ULONG i;
523
524 Status = FLT_PREOP_SUCCESS_NO_CALLBACK;
525 MajorFunction = Data->Iopb->MajorFunction;
526
527 for (i = 0; i < CallbacksCount; i++)
528 {
529 if (MajorFunction == Callbacks[i].MajorFunction)
530 {
531 // Call their pre-callback
532 Status = Callbacks[i].PreOperation(Data,
533 FltObjects,
534 CompletionContext);
535 }
536 }
537
538 return Status;
539 }
540
541 FLT_POSTOP_CALLBACK_STATUS
542 FLTAPI
FilterPostOperation(_Inout_ PFLT_CALLBACK_DATA Data,_In_ PCFLT_RELATED_OBJECTS FltObjects,_In_opt_ PVOID CompletionContext,_In_ FLT_POST_OPERATION_FLAGS Flags)543 FilterPostOperation(
544 _Inout_ PFLT_CALLBACK_DATA Data,
545 _In_ PCFLT_RELATED_OBJECTS FltObjects,
546 _In_opt_ PVOID CompletionContext,
547 _In_ FLT_POST_OPERATION_FLAGS Flags)
548 {
549 FLT_POSTOP_CALLBACK_STATUS Status;
550 UCHAR MajorFunction;
551 ULONG i;
552
553 Status = FLT_POSTOP_FINISHED_PROCESSING;
554 MajorFunction = Data->Iopb->MajorFunction;
555
556 for (i = 0; i < CallbacksCount; i++)
557 {
558 if (MajorFunction == Callbacks[i].MajorFunction)
559 {
560 // Call their post-callback
561 Status = Callbacks[i].PostOperation(Data,
562 FltObjects,
563 CompletionContext,
564 Flags);
565 }
566 }
567
568 return Status;
569 }
570