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