[FASTFAT] Fix size checking in VfatGetFileNameInformation()
[reactos.git] / 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 #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 NTSTATUS
25 FltpStartingToDrainObject(
26 _Inout_ PFLT_OBJECT Object
27 );
28
29 VOID
30 FltpMiniFilterDriverUnload(
31 );
32
33 static
34 NTSTATUS
35 GetFilterAltitude(
36 _In_ PFLT_FILTER Filter,
37 _Inout_ PUNICODE_STRING AltitudeString
38 );
39
40
41 /* EXPORTED FUNCTIONS ******************************************************/
42
43 NTSTATUS
44 NTAPI
45 FltLoadFilter(_In_ PCUNICODE_STRING FilterName)
46 {
47 UNICODE_STRING DriverServiceName;
48 UNICODE_STRING ServicesKey;
49 CHAR Buffer[MAX_KEY_LENGTH];
50
51 /* Setup the base services key */
52 RtlInitUnicodeString(&ServicesKey, SERVICES_KEY);
53
54 /* Initialize the string data */
55 DriverServiceName.Length = 0;
56 DriverServiceName.Buffer = (PWCH)Buffer;
57 DriverServiceName.MaximumLength = MAX_KEY_LENGTH;
58
59 /* Create the full service key for this filter */
60 RtlCopyUnicodeString(&DriverServiceName, &ServicesKey);
61 RtlAppendUnicodeStringToString(&DriverServiceName, FilterName);
62
63 /* Ask the kernel to load it for us */
64 return ZwLoadDriver(&DriverServiceName);
65 }
66
67 NTSTATUS
68 NTAPI
69 FltUnloadFilter(_In_ PCUNICODE_STRING FilterName)
70 {
71 //
72 //FIXME: This is a temp hack, it needs properly implementing
73 //
74
75 UNICODE_STRING DriverServiceName;
76 UNICODE_STRING ServicesKey;
77 CHAR Buffer[MAX_KEY_LENGTH];
78
79 /* Setup the base services key */
80 RtlInitUnicodeString(&ServicesKey, SERVICES_KEY);
81
82 /* Initialize the string data */
83 DriverServiceName.Length = 0;
84 DriverServiceName.Buffer = (PWCH)Buffer;
85 DriverServiceName.MaximumLength = MAX_KEY_LENGTH;
86
87 /* Create the full service key for this filter */
88 RtlCopyUnicodeString(&DriverServiceName, &ServicesKey);
89 RtlAppendUnicodeStringToString(&DriverServiceName, FilterName);
90 return ZwUnloadDriver(&DriverServiceName);
91 }
92
93 NTSTATUS
94 NTAPI
95 FltRegisterFilter(_In_ PDRIVER_OBJECT DriverObject,
96 _In_ const FLT_REGISTRATION *Registration,
97 _Out_ PFLT_FILTER *RetFilter)
98 {
99 PFLT_OPERATION_REGISTRATION Callbacks;
100 PFLT_FILTER Filter;
101 ULONG CallbackBufferSize;
102 ULONG FilterBufferSize;
103 ULONG Count = 0;
104 PCHAR Ptr;
105 NTSTATUS Status;
106
107 *RetFilter = NULL;
108
109 /* Make sure we're targeting the correct major revision */
110 if ((Registration->Version & 0xFF00) != FLT_MAJOR_VERSION)
111 {
112 return STATUS_INVALID_PARAMETER;
113 }
114
115 /* Make sure our namespace callbacks are valid */
116 if ((!Registration->GenerateFileNameCallback && Registration->NormalizeNameComponentCallback) ||
117 (!Registration->NormalizeNameComponentCallback && Registration->NormalizeContextCleanupCallback))
118 {
119 return STATUS_INVALID_PARAMETER;
120 }
121
122 /* Count the number of operations that were requested */
123 Callbacks = (PFLT_OPERATION_REGISTRATION)Registration->OperationRegistration;
124 while (Callbacks)
125 {
126 Count++;
127
128 /* Bail when we find the last one */
129 if (Callbacks->MajorFunction == IRP_MJ_OPERATION_END)
130 break;
131
132 /* Move to the next item */
133 Callbacks++;
134 }
135
136 /* Calculate the buffer sizes */
137 CallbackBufferSize = Count * sizeof(FLT_OPERATION_REGISTRATION);
138 FilterBufferSize = sizeof(FLT_FILTER) +
139 CallbackBufferSize +
140 DriverObject->DriverExtension->ServiceKeyName.Length;
141
142 /* Allocate a buffer to hold our filter data */
143 Filter = ExAllocatePoolWithTag(NonPagedPool,
144 FilterBufferSize,
145 FM_TAG_FILTER);
146 if (Filter == NULL) return STATUS_INSUFFICIENT_RESOURCES;
147 RtlZeroMemory(Filter, FilterBufferSize);
148
149 /* Find the end of the fixed struct */
150 Ptr = (PCHAR)(Filter + 1);
151
152 /* Store a copy of the driver object of this filter */
153 Filter->DriverObject = DriverObject;
154
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);
160
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;
170
171 /* Initialize the instance list */
172 ExInitializeResourceLite(&Filter->InstanceList.rLock);
173 InitializeListHead(&Filter->InstanceList.rList);
174 Filter->InstanceList.rCount = 0;
175
176 ExInitializeFastMutex(&Filter->ActiveOpens.mLock);
177 InitializeListHead(&Filter->ActiveOpens.mList);
178 Filter->ActiveOpens.mCount = 0;
179
180 ExInitializeFastMutex(&Filter->ConnectionList.mLock);
181 InitializeListHead(&Filter->ConnectionList.mList);
182 Filter->ConnectionList.mCount = 0;
183
184 /* Initialize the usermode port list */
185 ExInitializeFastMutex(&Filter->PortList.mLock);
186 InitializeListHead(&Filter->PortList.mList);
187 Filter->PortList.mCount = 0;
188
189 /* We got this far, assume success from here */
190 Status = STATUS_SUCCESS;
191
192 /* Check if the caller requested any context data */
193 if (Registration->ContextRegistration)
194 {
195 /* Register the contexts for this filter */
196 Status = FltpRegisterContexts(Filter, Registration->ContextRegistration);
197 if (NT_SUCCESS(Status))
198 {
199 goto Quit;
200 }
201 }
202
203 /* Check if the caller is registering any callbacks */
204 if (Registration->OperationRegistration)
205 {
206 /* The callback data comes after the fixed struct */
207 Filter->Operations = (PFLT_OPERATION_REGISTRATION)Ptr;
208 Ptr += (Count * sizeof(FLT_OPERATION_REGISTRATION));
209
210 /* Tag the operation data onto the end of the filter data */
211 RtlCopyMemory(Filter->Operations, Registration->OperationRegistration, CallbackBufferSize);
212
213 /* walk through the requested callbacks */
214 for (Callbacks = Filter->Operations;
215 Callbacks->MajorFunction != IRP_MJ_OPERATION_END;
216 Callbacks++)
217 {
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)
221 {
222 Filter->PreVolumeMount = Callbacks->PreOperation;
223 Filter->PostVolumeMount = Callbacks->PostOperation;
224 }
225 else if (Callbacks->MajorFunction == IRP_MJ_SHUTDOWN)
226 {
227 Callbacks->PostOperation = NULL;
228 }
229 }
230 }
231
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);
237
238 Status = GetFilterAltitude(Filter, &Filter->DefaultAltitude);
239 if (!NT_SUCCESS(Status))
240 {
241 goto Quit;
242 }
243
244 //
245 // - Slot the filter into the correct altitude location
246 // - More stuff??
247 //
248
249 /* Store any existing driver unload routine before we make any changes */
250 Filter->OldDriverUnload = (PFLT_FILTER_UNLOAD_CALLBACK)DriverObject->DriverUnload;
251
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))
254 {
255 DriverObject->DriverUnload = (PDRIVER_UNLOAD)FltpMiniFilterDriverUnload;
256 }
257 else
258 {
259 DriverObject->DriverUnload = (PDRIVER_UNLOAD)NULL;
260 }
261
262
263 Quit:
264
265 if (NT_SUCCESS(Status))
266 {
267 DPRINT1("Loaded FS mini-filter %wZ\n", &DriverObject->DriverExtension->ServiceKeyName);
268 *RetFilter = Filter;
269 }
270 else
271 {
272 DPRINT1("Failed to load FS mini-filter %wZ : 0x%X\n", &DriverObject->DriverExtension->ServiceKeyName, Status);
273
274 // Add cleanup for context resources
275
276 ExDeleteResourceLite(&Filter->InstanceList.rLock);
277 ExFreePoolWithTag(Filter, FM_TAG_FILTER);
278 }
279
280 return Status;
281 }
282
283 VOID
284 FLTAPI
285 FltUnregisterFilter(_In_ PFLT_FILTER Filter)
286 {
287 PFLT_INSTANCE Instance;
288 PLIST_ENTRY CurrentEntry;
289 NTSTATUS Status;
290
291 /* Set the draining flag */
292 Status = FltpStartingToDrainObject(&Filter->Base);
293 if (!NT_SUCCESS(Status))
294 {
295 /* Someone already unregistered us, just remove our ref and bail */
296 FltObjectDereference(&Filter->Base);
297 return;
298 }
299
300 /* Lock the instance list */
301 KeEnterCriticalRegion();
302 ExAcquireResourceSharedLite(&Filter->InstanceList.rLock, TRUE);
303
304 /* Set the first entry in the list */
305 CurrentEntry = Filter->InstanceList.rList.Flink;
306
307 /* Free all instances referenced by the filter */
308 while (CurrentEntry != &Filter->InstanceList.rList)
309 {
310 /* Get the record pointer */
311 Instance = CONTAINING_RECORD(CurrentEntry, FLT_INSTANCE, FilterLink);
312
313 // FIXME: implement
314 (void)Instance;
315
316 /* Reset the pointer and move to next entry */
317 Instance = NULL;
318 CurrentEntry = CurrentEntry->Flink;
319 }
320
321 /* We're done with instances now */
322 ExReleaseResourceLite(&Filter->InstanceList.rLock);
323 KeLeaveCriticalRegion();
324
325 /* Remove the reference from the base object */
326 FltObjectDereference(&Filter->Base);
327
328 /* Wait until we're sure nothing is using the filter */
329 FltpObjectRundownWait(&Filter->Base.RundownRef);
330
331 /* Delete the instance list lock */
332 ExDeleteResourceLite(&Filter->InstanceList.rLock);
333
334 /* We're finished cleaning up now */
335 FltpExRundownCompleted(&Filter->Base.RundownRef);
336
337 /* Hand the memory back */
338 ExFreePoolWithTag(Filter, FM_TAG_FILTER);
339 }
340
341 NTSTATUS
342 NTAPI
343 FltStartFiltering(_In_ PFLT_FILTER Filter)
344 {
345 NTSTATUS Status;
346
347 /* Grab a ref to the filter */
348 Status = FltObjectReference(&Filter->Base);
349 if (NT_SUCCESS(Status))
350 {
351 /* Make sure we aren't already starting up */
352 if (!(Filter->Flags & FLTFL_FILTERING_INITIATED))
353 {
354 // Startup
355 }
356 else
357 {
358 Status = STATUS_INVALID_PARAMETER;
359 }
360
361 FltObjectDereference(&Filter->Base);
362 }
363
364 return Status;
365 }
366
367
368 /* INTERNAL FUNCTIONS ******************************************************/
369
370 NTSTATUS
371 FltpStartingToDrainObject(_Inout_ PFLT_OBJECT Object)
372 {
373 /*
374 * Set the draining flag for the filter. This let's us force
375 * a post op callback for minifilters currently awaiting one.
376 */
377 if (InterlockedOr((PLONG)&Object->Flags, FLT_OBFL_DRAINING) & 1)
378 {
379 /* We've been called once, we're already being deleted */
380 return STATUS_FLT_DELETING_OBJECT;
381 }
382
383 return STATUS_SUCCESS;
384 }
385
386 VOID
387 FltpMiniFilterDriverUnload()
388 {
389 __debugbreak();
390 }
391
392 /* PRIVATE FUNCTIONS ******************************************************/
393
394 static
395 NTSTATUS
396 GetFilterAltitude(
397 _In_ PFLT_FILTER Filter,
398 _Inout_ PUNICODE_STRING AltitudeString)
399 {
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;
405 ULONG BytesRequired;
406 HANDLE InstHandle = NULL;
407 HANDLE RootHandle;
408 PWCH InstBuffer = NULL;
409 PWCH AltBuffer = NULL;
410 NTSTATUS Status;
411
412 /* Get a handle to the instances key in the filter's services key */
413 Status = FltpOpenFilterServicesKey(Filter,
414 KEY_QUERY_VALUE,
415 &InstancesKey,
416 &RootHandle);
417 if (!NT_SUCCESS(Status))
418 {
419 return Status;
420 }
421
422 /* Read the size 'default instances' string value */
423 Status = FltpReadRegistryValue(RootHandle,
424 &DefaultInstance,
425 REG_SZ,
426 NULL,
427 0,
428 &BytesRequired);
429
430 /* We should get a buffer too small error */
431 if (Status == STATUS_BUFFER_TOO_SMALL)
432 {
433 /* Allocate the buffer we need to hold the string */
434 InstBuffer = ExAllocatePoolWithTag(PagedPool, BytesRequired, FM_TAG_UNICODE_STRING);
435 if (InstBuffer == NULL)
436 {
437 Status = STATUS_INSUFFICIENT_RESOURCES;
438 goto Quit;
439 }
440
441 /* Now read the string value */
442 Status = FltpReadRegistryValue(RootHandle,
443 &DefaultInstance,
444 REG_SZ,
445 InstBuffer,
446 BytesRequired,
447 &BytesRequired);
448 }
449
450 if (!NT_SUCCESS(Status))
451 {
452 goto Quit;
453 }
454
455 /* Convert the string to a unicode_string */
456 RtlInitUnicodeString(&FilterInstancePath, InstBuffer);
457
458 /* Setup the attributes using the root key handle */
459 InitializeObjectAttributes(&ObjectAttributes,
460 &FilterInstancePath,
461 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
462 RootHandle,
463 NULL);
464
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))
468 {
469 /* Get the size of the buffer that holds the altitude */
470 Status = FltpReadRegistryValue(InstHandle,
471 &Altitude,
472 REG_SZ,
473 NULL,
474 0,
475 &BytesRequired);
476 if (Status == STATUS_BUFFER_TOO_SMALL)
477 {
478 /* Allocate the required buffer */
479 AltBuffer = ExAllocatePoolWithTag(PagedPool, BytesRequired, FM_TAG_UNICODE_STRING);
480 if (AltBuffer == NULL)
481 {
482 Status = STATUS_INSUFFICIENT_RESOURCES;
483 goto Quit;
484 }
485
486 /* And now finally read in the actual altitude string */
487 Status = FltpReadRegistryValue(InstHandle,
488 &Altitude,
489 REG_SZ,
490 AltBuffer,
491 BytesRequired,
492 &BytesRequired);
493 if (NT_SUCCESS(Status))
494 {
495 /* We made it, setup the return buffer */
496 AltitudeString->Length = BytesRequired;
497 AltitudeString->MaximumLength = BytesRequired;
498 AltitudeString->Buffer = AltBuffer;
499 }
500 }
501 }
502
503 Quit:
504 if (!NT_SUCCESS(Status))
505 {
506 if (AltBuffer)
507 {
508 ExFreePoolWithTag(AltBuffer, FM_TAG_UNICODE_STRING);
509 }
510 }
511
512 if (InstBuffer)
513 {
514 ExFreePoolWithTag(InstBuffer, FM_TAG_UNICODE_STRING);
515 }
516
517 if (InstHandle)
518 {
519 ZwClose(InstHandle);
520 }
521 ZwClose(RootHandle);
522
523 return Status;
524 }
525
526
527
528 NTSTATUS
529 FltpReadRegistryValue(
530 _In_ HANDLE KeyHandle,
531 _In_ PUNICODE_STRING ValueName,
532 _In_opt_ ULONG Type,
533 _Out_writes_bytes_(BufferSize) PVOID Buffer,
534 _In_ ULONG BufferSize,
535 _Out_opt_ PULONG BytesRequired
536 );