xref: /reactos/drivers/filters/fltmgr/Messaging.c (revision 682f85ad)
1 /*
2 * PROJECT:         Filesystem Filter Manager
3 * LICENSE:         GPL - See COPYING in the top level directory
4 * FILE:            drivers/filters/fltmgr/Messaging.c
5 * PURPOSE:         Contains the routines to handle the comms layer
6 * PROGRAMMERS:     Ged Murphy (gedmurphy@reactos.org)
7 */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "fltmgr.h"
12 #include "fltmgrint.h"
13 #include <fltmgr_shared.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 
19 /* DATA *********************************************************************/
20 
21 UNICODE_STRING CommsDeviceName = RTL_CONSTANT_STRING(L"\\FileSystem\\Filters\\FltMgrMsg");
22 PDEVICE_OBJECT CommsDeviceObject;
23 
24 POBJECT_TYPE ServerPortObjectType;
25 POBJECT_TYPE ClientPortObjectType;
26 
27 static
28 BOOLEAN
29 FltpDisconnectPort(
30     _In_ PFLT_PORT_OBJECT PortObject
31 );
32 
33 static
34 NTSTATUS
35 CreateClientPort(
36     _In_ PFILE_OBJECT FileObject,
37     _Inout_ PIRP Irp
38 );
39 
40 static
41 NTSTATUS
42 CloseClientPort(
43     _In_ PFILE_OBJECT FileObject,
44     _Inout_ PIRP Irp
45 );
46 
47 static
48 NTSTATUS
49 InitializeMessageWaiterQueue(
50     _Inout_ PFLT_MESSAGE_WAITER_QUEUE MsgWaiterQueue
51 );
52 
53 static
54 PPORT_CCB
55 CreatePortCCB(
56     _In_ PFLT_PORT_OBJECT PortObject
57 );
58 
59 
60 
61 /* EXPORTED FUNCTIONS ******************************************************/
62 
63 _Must_inspect_result_
64 _IRQL_requires_max_(PASSIVE_LEVEL)
65 NTSTATUS
66 FLTAPI
67 FltCreateCommunicationPort(_In_ PFLT_FILTER Filter,
68                            _Outptr_ PFLT_PORT *ServerPort,
69                            _In_ POBJECT_ATTRIBUTES ObjectAttributes,
70                            _In_opt_ PVOID ServerPortCookie,
71                            _In_ PFLT_CONNECT_NOTIFY ConnectNotifyCallback,
72                            _In_ PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,
73                            _In_opt_ PFLT_MESSAGE_NOTIFY MessageNotifyCallback,
74                            _In_ LONG MaxConnections)
75 {
76     PFLT_SERVER_PORT_OBJECT PortObject;
77     NTSTATUS Status;
78 
79     /* The caller must allow at least one connection */
80     if (MaxConnections == 0)
81     {
82         return STATUS_INVALID_PARAMETER;
83     }
84 
85     /* The request must be for a kernel handle */
86     if (!(ObjectAttributes->Attributes & OBJ_KERNEL_HANDLE))
87     {
88         return STATUS_INVALID_PARAMETER;
89     }
90 
91     /*
92      * Get rundown protection on the target to stop the owner
93      * from unloading whilst this port object is open. It gets
94      * removed in the FltpServerPortClose callback
95      */
96     Status = FltObjectReference(Filter);
97     if (!NT_SUCCESS(Status))
98     {
99         return Status;
100     }
101 
102     /* Create the server port object for this filter */
103     Status = ObCreateObject(KernelMode,
104                             ServerPortObjectType,
105                             ObjectAttributes,
106                             KernelMode,
107                             NULL,
108                             sizeof(FLT_SERVER_PORT_OBJECT),
109                             0,
110                             0,
111                             (PVOID *)&PortObject);
112     if (NT_SUCCESS(Status))
113     {
114         /* Zero out the struct */
115         RtlZeroMemory(PortObject, sizeof(FLT_SERVER_PORT_OBJECT));
116 
117         /* Increment the ref count on the target filter */
118         FltpObjectPointerReference((PFLT_OBJECT)Filter);
119 
120         /* Setup the filter port object */
121         PortObject->Filter = Filter;
122         PortObject->ConnectNotify = ConnectNotifyCallback;
123         PortObject->DisconnectNotify = DisconnectNotifyCallback;
124         PortObject->MessageNotify = MessageNotifyCallback;
125         PortObject->Cookie = ServerPortCookie;
126         PortObject->MaxConnections = MaxConnections;
127 
128         /* Insert the object */
129         Status = ObInsertObject(PortObject,
130                                 NULL,
131                                 STANDARD_RIGHTS_ALL | FILE_READ_DATA,
132                                 0,
133                                 NULL,
134                                 (PHANDLE)ServerPort);
135         if (NT_SUCCESS(Status))
136         {
137             /* Lock the connection list */
138             ExAcquireFastMutex(&Filter->ConnectionList.mLock);
139 
140             /* Add the new port object to the connection list and increment the count */
141             InsertTailList(&Filter->ConnectionList.mList, &PortObject->FilterLink);
142             Filter->ConnectionList.mCount++;
143 
144             /* Unlock the connection list*/
145             ExReleaseFastMutex(&Filter->ConnectionList.mLock);
146         }
147     }
148 
149     if (!NT_SUCCESS(Status))
150     {
151         /* Allow the filter to be cleaned up */
152         FltObjectDereference(Filter);
153     }
154 
155     return Status;
156 }
157 
158 _IRQL_requires_max_(PASSIVE_LEVEL)
159 VOID
160 FLTAPI
161 FltCloseCommunicationPort(_In_ PFLT_PORT ServerPort)
162 {
163     /* Just close the handle to initiate the cleanup callbacks */
164     ZwClose(ServerPort);
165 }
166 
167 _IRQL_requires_max_(PASSIVE_LEVEL)
168 VOID
169 FLTAPI
170 FltCloseClientPort(_In_ PFLT_FILTER Filter,
171                    _Inout_ PFLT_PORT *ClientPort)
172 {
173     PFLT_PORT Port;
174 
175     /* Protect against the handle being used whilst we're closing it */
176     FltAcquirePushLockShared(&Filter->PortLock);
177 
178     /* Store the port handle while we have the lock held */
179     Port = *ClientPort;
180 
181     if (*ClientPort)
182     {
183         /* Set the hadle to null */
184         *ClientPort = NULL;
185     }
186 
187     /* Unlock the port */
188     FltReleasePushLock(&Filter->PortLock);
189 
190     if (Port)
191     {
192         /* Close the safe handle */
193         ZwClose(Port);
194     }
195 }
196 
197 _Must_inspect_result_
198 _IRQL_requires_max_(APC_LEVEL)
199 NTSTATUS
200 FLTAPI
201 FltSendMessage(_In_ PFLT_FILTER Filter,
202                _In_ PFLT_PORT *ClientPort,
203                _In_reads_bytes_(SenderBufferLength) PVOID SenderBuffer,
204                _In_ ULONG SenderBufferLength,
205                _Out_writes_bytes_opt_(*ReplyLength) PVOID ReplyBuffer,
206                _Inout_opt_ PULONG ReplyLength,
207                _In_opt_ PLARGE_INTEGER Timeout)
208 {
209     UNREFERENCED_PARAMETER(Filter);
210     UNREFERENCED_PARAMETER(ClientPort);
211     UNREFERENCED_PARAMETER(SenderBuffer);
212     UNREFERENCED_PARAMETER(SenderBufferLength);
213     UNREFERENCED_PARAMETER(ReplyBuffer);
214     UNREFERENCED_PARAMETER(ReplyLength);
215     UNREFERENCED_PARAMETER(Timeout);
216     return STATUS_NOT_IMPLEMENTED;
217 }
218 
219 /* INTERNAL FUNCTIONS ******************************************************/
220 
221 
222 NTSTATUS
223 FltpMsgCreate(_In_ PDEVICE_OBJECT DeviceObject,
224               _Inout_ PIRP Irp)
225 {
226     PIO_STACK_LOCATION StackPtr;
227     NTSTATUS Status;
228 
229     /* Get the stack location */
230     StackPtr = IoGetCurrentIrpStackLocation(Irp);
231 
232     FLT_ASSERT(StackPtr->MajorFunction == IRP_MJ_CREATE);
233 
234     /* Check if this is a caller wanting to connect */
235     if (StackPtr->MajorFunction == IRP_MJ_CREATE)
236     {
237         /* Create the client port for this connection and exit */
238         Status = CreateClientPort(StackPtr->FileObject, Irp);
239     }
240     else
241     {
242         Status = STATUS_INVALID_PARAMETER;
243     }
244 
245     if (Status != STATUS_PENDING)
246     {
247         Irp->IoStatus.Status = Status;
248         Irp->IoStatus.Information = 0;
249         IoCompleteRequest(Irp, 0);
250     }
251 
252     return Status;
253 }
254 
255 NTSTATUS
256 FltpMsgDispatch(_In_ PDEVICE_OBJECT DeviceObject,
257                 _Inout_ PIRP Irp)
258 {
259     PIO_STACK_LOCATION StackPtr;
260     NTSTATUS Status;
261 
262     /* Get the stack location */
263     StackPtr = IoGetCurrentIrpStackLocation(Irp);
264 
265     /* Check if this is a caller wanting to connect */
266     if (StackPtr->MajorFunction == IRP_MJ_CLOSE)
267     {
268         /* Create the client port for this connection and exit */
269         Status = CloseClientPort(StackPtr->FileObject, Irp);
270     }
271     else
272     {
273         // We don't support anything else yet
274         Status = STATUS_NOT_IMPLEMENTED;
275     }
276 
277     if (Status != STATUS_PENDING)
278     {
279         Irp->IoStatus.Status = Status;
280         Irp->IoStatus.Information = 0;
281         IoCompleteRequest(Irp, 0);
282     }
283 
284     return Status;
285 }
286 
287 VOID
288 NTAPI
289 FltpServerPortClose(_In_opt_ PEPROCESS Process,
290                     _In_ PVOID Object,
291                     _In_ ACCESS_MASK GrantedAccess,
292                     _In_ ULONG ProcessHandleCount,
293                     _In_ ULONG SystemHandleCount)
294 {
295     PFLT_SERVER_PORT_OBJECT PortObject;
296     PFAST_MUTEX Lock;
297 
298     /* Cast the object to a server port object */
299     PortObject = (PFLT_SERVER_PORT_OBJECT)Object;
300 
301     /* Lock the connection list */
302     Lock = &PortObject->Filter->ConnectionList.mLock;
303     ExAcquireFastMutex(Lock);
304 
305     /* Remove the server port object from the list */
306     RemoveEntryList(&PortObject->FilterLink);
307 
308     /* Unlock the connection list */
309     ExReleaseFastMutex(Lock);
310 
311     /* Remove the rundown protection we added to stop the owner from tearing us down */
312     FltObjectDereference(PortObject->Filter);
313 }
314 
315 VOID
316 NTAPI
317 FltpServerPortDelete(PVOID Object)
318 {
319     /* Decrement the filter count we added in the create routine */
320     PFLT_SERVER_PORT_OBJECT PortObject = (PFLT_SERVER_PORT_OBJECT)Object;
321     FltpObjectPointerDereference((PFLT_OBJECT)PortObject->Filter);
322 
323 }
324 
325 VOID
326 NTAPI
327 FltpClientPortClose(_In_opt_ PEPROCESS Process,
328                     _In_ PVOID Object,
329                     _In_ ACCESS_MASK GrantedAccess,
330                     _In_ ULONG ProcessHandleCount,
331                     _In_ ULONG SystemHandleCount)
332 {
333     PFLT_PORT_OBJECT PortObject = (PFLT_PORT_OBJECT)Object;
334 
335     if (FltpDisconnectPort(PortObject))
336     {
337         InterlockedDecrement(&PortObject->ServerPort->NumberOfConnections);
338     }
339 }
340 
341 BOOLEAN
342 FltpDisconnectPort(_In_ PFLT_PORT_OBJECT PortObject)
343 {
344     BOOLEAN Disconnected = FALSE;
345 
346     /* Lock the port object while we disconnect it */
347     ExAcquireFastMutex(&PortObject->Lock);
348 
349     /* Make sure we have a valid connection */
350     if (PortObject->Disconnected == FALSE)
351     {
352         /* Let any waiters know we're dusconnecing */
353         KeSetEvent(&PortObject->DisconnectEvent, 0, 0);
354 
355         // cleanup everything in the message queue (PortObject->MsgQ.Csq)
356 
357         /* Set the disconnected state to true */
358         PortObject->Disconnected = TRUE;
359         Disconnected = TRUE;
360     }
361 
362     /* Unlock and exit*/
363     ExReleaseFastMutex(&PortObject->Lock);
364     return Disconnected;
365 }
366 
367 VOID
368 NTAPI
369 FltpClientPortDelete(PVOID Object)
370 {
371     PFLT_PORT_OBJECT PortObject = (PFLT_PORT_OBJECT)Object;
372     ObDereferenceObject(PortObject->ServerPort);
373 }
374 
375 
376 NTSTATUS
377 FltpSetupCommunicationObjects(_In_ PDRIVER_OBJECT DriverObject)
378 {
379     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
380     UNICODE_STRING SymLinkName;
381     UNICODE_STRING Name;
382     NTSTATUS Status;
383 
384     GENERIC_MAPPING Mapping =
385     {
386         STANDARD_RIGHTS_READ,
387         STANDARD_RIGHTS_WRITE,
388         STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
389         FLT_PORT_ALL_ACCESS
390     };
391 
392     /* Create the server comms object type */
393     RtlZeroMemory(&ObjectTypeInitializer, sizeof(OBJECT_TYPE_INITIALIZER));
394     RtlInitUnicodeString(&Name, L"FilterConnectionPort");
395     ObjectTypeInitializer.Length = sizeof(OBJECT_TYPE_INITIALIZER);
396     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
397     ObjectTypeInitializer.GenericMapping = Mapping;
398     ObjectTypeInitializer.PoolType = NonPagedPool;
399     ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(FLT_SERVER_PORT_OBJECT);
400     ObjectTypeInitializer.ValidAccessMask = GENERIC_ALL;
401     ObjectTypeInitializer.CloseProcedure = FltpServerPortClose;
402     ObjectTypeInitializer.DeleteProcedure = FltpServerPortDelete;
403     Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ServerPortObjectType);
404     if (!NT_SUCCESS(Status)) return Status;
405 
406     /* Create the client comms object type */
407     RtlZeroMemory(&ObjectTypeInitializer, sizeof(OBJECT_TYPE_INITIALIZER));
408     RtlInitUnicodeString(&Name, L"FilterCommunicationPort");
409     ObjectTypeInitializer.Length = sizeof(OBJECT_TYPE_INITIALIZER);
410     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
411     ObjectTypeInitializer.GenericMapping = Mapping;
412     ObjectTypeInitializer.PoolType = NonPagedPool;
413     ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(FLT_PORT_OBJECT);
414     ObjectTypeInitializer.ValidAccessMask = GENERIC_ALL;
415     ObjectTypeInitializer.CloseProcedure = FltpClientPortClose;
416     ObjectTypeInitializer.DeleteProcedure = FltpClientPortDelete;
417     Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ClientPortObjectType);
418     if (!NT_SUCCESS(Status))
419     {
420         goto Quit;
421     }
422 
423     /* Create the device object */
424     Status = IoCreateDevice(DriverObject,
425                             0,
426                             &CommsDeviceName,
427                             FILE_DEVICE_WPD,
428                             0,
429                             0,
430                             &CommsDeviceObject);
431     if (NT_SUCCESS(Status))
432     {
433         /* Setup a symbolic link for the device */
434         RtlInitUnicodeString(&SymLinkName, L"\\DosDevices\\FltMgrMsg");
435         Status = IoCreateSymbolicLink(&SymLinkName, &CommsDeviceName);
436     }
437 
438 Quit:
439     if (!NT_SUCCESS(Status))
440     {
441         /* Something went wrong, undo */
442         if (CommsDeviceObject)
443         {
444             IoDeleteDevice(CommsDeviceObject);
445             CommsDeviceObject = NULL;
446         }
447         if (ClientPortObjectType)
448         {
449             ObMakeTemporaryObject(ClientPortObjectType);
450             ObDereferenceObject(ClientPortObjectType);
451             ClientPortObjectType = NULL;
452         }
453 
454         if (ServerPortObjectType)
455         {
456             ObMakeTemporaryObject(ServerPortObjectType);
457             ObDereferenceObject(ServerPortObjectType);
458             ServerPortObjectType = NULL;
459         }
460     }
461 
462     return Status;
463 }
464 
465 /* CSQ IRP CALLBACKS *******************************************************/
466 
467 
468 NTSTATUS
469 NTAPI
470 FltpAddMessageWaiter(_In_ PIO_CSQ Csq,
471                      _In_ PIRP Irp,
472                      _In_ PVOID InsertContext)
473 {
474     PFLT_MESSAGE_WAITER_QUEUE MessageWaiterQueue;
475 
476     /* Get the start of the waiter queue struct */
477     MessageWaiterQueue = CONTAINING_RECORD(Csq,
478                                            FLT_MESSAGE_WAITER_QUEUE,
479                                            Csq);
480 
481     /* Insert the IRP at the end of the queue */
482     InsertTailList(&MessageWaiterQueue->WaiterQ.mList,
483                    &Irp->Tail.Overlay.ListEntry);
484 
485     /* return success */
486     return STATUS_SUCCESS;
487 }
488 
489 VOID
490 NTAPI
491 FltpRemoveMessageWaiter(_In_ PIO_CSQ Csq,
492                         _In_ PIRP Irp)
493 {
494     /* Remove the IRP from the queue */
495     RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
496 }
497 
498 PIRP
499 NTAPI
500 FltpGetNextMessageWaiter(_In_ PIO_CSQ Csq,
501                          _In_ PIRP Irp,
502                          _In_ PVOID PeekContext)
503 {
504     PFLT_MESSAGE_WAITER_QUEUE MessageWaiterQueue;
505     PIRP NextIrp = NULL;
506     PLIST_ENTRY NextEntry;
507     PIO_STACK_LOCATION IrpStack;
508 
509     /* Get the start of the waiter queue struct */
510     MessageWaiterQueue = CONTAINING_RECORD(Csq,
511                                            FLT_MESSAGE_WAITER_QUEUE,
512                                            Csq);
513 
514     /* Is the IRP valid? */
515     if (Irp == NULL)
516     {
517         /* Start peeking from the listhead */
518         NextEntry = MessageWaiterQueue->WaiterQ.mList.Flink;
519     }
520     else
521     {
522         /* Start peeking from that IRP onwards */
523         NextEntry = Irp->Tail.Overlay.ListEntry.Flink;
524     }
525 
526     /* Loop through the queue */
527     while (NextEntry != &MessageWaiterQueue->WaiterQ.mList)
528     {
529         /* Store the next IRP in the list */
530         NextIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
531 
532         /* Did we supply a PeekContext on insert? */
533         if (!PeekContext)
534         {
535             /* We already have the next IRP */
536             break;
537         }
538         else
539         {
540             /* Get the stack of the next IRP */
541             IrpStack = IoGetCurrentIrpStackLocation(NextIrp);
542 
543             /* Does the PeekContext match the object? */
544             if (IrpStack->FileObject == (PFILE_OBJECT)PeekContext)
545             {
546                 /* We have a match */
547                 break;
548             }
549 
550             /* Move to the next IRP */
551             NextIrp = NULL;
552             NextEntry = NextEntry->Flink;
553         }
554     }
555 
556     return NextIrp;
557 }
558 
559 _Acquires_lock_(((PFLT_MESSAGE_WAITER_QUEUE)CONTAINING_RECORD(Csq, FLT_MESSAGE_WAITER_QUEUE, Csq))->WaiterQ.mLock)
560 _IRQL_saves_global_(Irql, ((PFLT_MESSAGE_WAITER_QUEUE)CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue))->WaiterQ.mLock)
561 _IRQL_raises_(DISPATCH_LEVEL)
562 VOID
563 NTAPI
564 FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq,
565                              _Out_ PKIRQL Irql)
566 {
567     PFLT_MESSAGE_WAITER_QUEUE MessageWaiterQueue;
568 
569     UNREFERENCED_PARAMETER(Irql);
570 
571     /* Get the start of the waiter queue struct */
572     MessageWaiterQueue = CONTAINING_RECORD(Csq,
573                                            FLT_MESSAGE_WAITER_QUEUE,
574                                            Csq);
575 
576     /* Acquire the IRP queue lock */
577     ExAcquireFastMutex(&MessageWaiterQueue->WaiterQ.mLock);
578 }
579 
580 _Releases_lock_(((PFLT_MESSAGE_WAITER_QUEUE)CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue))->WaiterQ.mLock)
581 _IRQL_restores_global_(Irql, ((PFLT_MESSAGE_WAITER_QUEUE)CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue))->WaiterQ.mLock)
582 _IRQL_requires_(DISPATCH_LEVEL)
583 VOID
584 NTAPI
585 FltpReleaseMessageWaiterLock(_In_ PIO_CSQ Csq,
586                              _In_ KIRQL Irql)
587 {
588     PFLT_MESSAGE_WAITER_QUEUE MessageWaiterQueue;
589 
590     UNREFERENCED_PARAMETER(Irql);
591 
592     /* Get the start of the waiter queue struct */
593     MessageWaiterQueue = CONTAINING_RECORD(Csq,
594                                            FLT_MESSAGE_WAITER_QUEUE,
595                                            Csq);
596 
597     /* Release the IRP queue lock */
598     ExReleaseFastMutex(&MessageWaiterQueue->WaiterQ.mLock);
599 }
600 
601 VOID
602 NTAPI
603 FltpCancelMessageWaiter(_In_ PIO_CSQ Csq,
604                         _In_ PIRP Irp)
605 {
606     /* Cancel the IRP */
607     Irp->IoStatus.Status = STATUS_CANCELLED;
608     Irp->IoStatus.Information = 0;
609     IoCompleteRequest(Irp, IO_NO_INCREMENT);
610 }
611 
612 
613 /* PRIVATE FUNCTIONS ******************************************************/
614 
615 static
616 NTSTATUS
617 CreateClientPort(_In_ PFILE_OBJECT FileObject,
618                    _Inout_ PIRP Irp)
619 {
620     PFLT_SERVER_PORT_OBJECT ServerPortObject = NULL;
621     OBJECT_ATTRIBUTES ObjectAttributes;
622     PFILTER_PORT_DATA FilterPortData;
623     PFLT_PORT_OBJECT ClientPortObject = NULL;
624     PFLT_PORT PortHandle = NULL;
625     PPORT_CCB PortCCB = NULL;
626     //ULONG BufferLength;
627     LONG NumConns;
628     NTSTATUS Status;
629 
630     /* We received the buffer via FilterConnectCommunicationPort, cast it back to its original form */
631     FilterPortData = Irp->AssociatedIrp.SystemBuffer;
632 
633     /* Get a reference to the server port the filter created */
634     Status = ObReferenceObjectByName(&FilterPortData->PortName,
635                                      0,
636                                      0,
637                                      FLT_PORT_ALL_ACCESS,
638                                      ServerPortObjectType,
639                                      ExGetPreviousMode(),
640                                      0,
641                                      (PVOID *)&ServerPortObject);
642     if (!NT_SUCCESS(Status))
643     {
644         return Status;
645     }
646 
647     /* Increment the number of connections on the server port */
648     NumConns = InterlockedIncrement(&ServerPortObject->NumberOfConnections);
649     if (NumConns > ServerPortObject->MaxConnections)
650     {
651         Status = STATUS_CONNECTION_COUNT_LIMIT;
652         goto Quit;
653     }
654 
655     /* Initialize a basic kernel handle request */
656     InitializeObjectAttributes(&ObjectAttributes,
657                                NULL,
658                                OBJ_KERNEL_HANDLE,
659                                NULL,
660                                NULL);
661 
662     /* Now create the new client port object */
663     Status = ObCreateObject(KernelMode,
664                             ClientPortObjectType,
665                             &ObjectAttributes,
666                             KernelMode,
667                             NULL,
668                             sizeof(FLT_PORT_OBJECT),
669                             0,
670                             0,
671                             (PVOID *)&ClientPortObject);
672     if (!NT_SUCCESS(Status))
673     {
674         goto Quit;
675     }
676 
677     /* Clear out the buffer */
678     RtlZeroMemory(ClientPortObject, sizeof(FLT_PORT_OBJECT));
679 
680     /* Initialize the locks */
681     ExInitializeRundownProtection(&ClientPortObject->MsgNotifRundownRef);
682     ExInitializeFastMutex(&ClientPortObject->Lock);
683 
684     /* Set the server port object this belongs to */
685     ClientPortObject->ServerPort = ServerPortObject;
686 
687     /* Setup the message queue */
688     Status = InitializeMessageWaiterQueue(&ClientPortObject->MsgQ);
689     if (!NT_SUCCESS(Status))
690     {
691         goto Quit;
692     }
693 
694     /* Create the CCB which we'll attach to the file object */
695     PortCCB = CreatePortCCB(ClientPortObject);
696     if (PortCCB == NULL)
697     {
698         Status = STATUS_INSUFFICIENT_RESOURCES;
699         goto Quit;
700     }
701 
702     /* Now insert the new client port into the object manager*/
703     Status = ObInsertObject(ClientPortObject, 0, FLT_PORT_ALL_ACCESS, 1, 0, (PHANDLE)&PortHandle);
704     if (!NT_SUCCESS(Status))
705     {
706         goto Quit;
707     }
708 
709     /* Add a reference to the filter to keep it alive while we do some work with it */
710     Status = FltObjectReference(ServerPortObject->Filter);
711     if (NT_SUCCESS(Status))
712     {
713         /* Invoke the callback to let the filter know we have a connection */
714         Status = ServerPortObject->ConnectNotify(PortHandle,
715                                                  ServerPortObject->Cookie,
716                                                  NULL, //ConnectionContext
717                                                  0, //SizeOfContext
718                                                  &ClientPortObject->Cookie);
719         if (NT_SUCCESS(Status))
720         {
721             /* Add the client port CCB to the file object */
722             FileObject->FsContext2 = PortCCB;
723 
724             /* Lock the port list on the filter and add this new port object to the list */
725             ExAcquireFastMutex(&ServerPortObject->Filter->PortList.mLock);
726             InsertTailList(&ServerPortObject->Filter->PortList.mList, &ClientPortObject->FilterLink);
727             ExReleaseFastMutex(&ServerPortObject->Filter->PortList.mLock);
728         }
729 
730         /* We're done with the filter object, decremement the count */
731         FltObjectDereference(ServerPortObject->Filter);
732     }
733 
734 
735 Quit:
736     if (!NT_SUCCESS(Status))
737     {
738         if (ClientPortObject)
739         {
740             ObDereferenceObject(ClientPortObject);
741         }
742 
743         if (PortHandle)
744         {
745             ZwClose(PortHandle);
746         }
747         else if (ServerPortObject)
748         {
749             InterlockedDecrement(&ServerPortObject->NumberOfConnections);
750             ObDereferenceObject(ServerPortObject);
751         }
752 
753         if (PortCCB)
754         {
755             ExFreePoolWithTag(PortCCB, FM_TAG_CCB);
756         }
757     }
758 
759     return Status;
760 }
761 
762 static
763 NTSTATUS
764 CloseClientPort(_In_ PFILE_OBJECT FileObject,
765                 _Inout_ PIRP Irp)
766 {
767     PFLT_CCB Ccb;
768 
769     Ccb = (PFLT_CCB)FileObject->FsContext2;
770 
771     /* Remove the reference on the filter we added when we opened the port */
772     ObDereferenceObject(Ccb->Data.Port.Port);
773 
774     // FIXME: Free the CCB
775 
776     return STATUS_SUCCESS;
777 }
778 
779 static
780 NTSTATUS
781 InitializeMessageWaiterQueue(_Inout_ PFLT_MESSAGE_WAITER_QUEUE MsgWaiterQueue)
782 {
783     NTSTATUS Status;
784 
785     /* Setup the IRP queue */
786     Status = IoCsqInitializeEx(&MsgWaiterQueue->Csq,
787                                FltpAddMessageWaiter,
788                                FltpRemoveMessageWaiter,
789                                FltpGetNextMessageWaiter,
790                                FltpAcquireMessageWaiterLock,
791                                FltpReleaseMessageWaiterLock,
792                                FltpCancelMessageWaiter);
793     if (!NT_SUCCESS(Status))
794     {
795         return Status;
796     }
797 
798     /* Initialize the waiter queue */
799     ExInitializeFastMutex(&MsgWaiterQueue->WaiterQ.mLock);
800     InitializeListHead(&MsgWaiterQueue->WaiterQ.mList);
801     MsgWaiterQueue->WaiterQ.mCount = 0;
802 
803     /* We don't have a minimum waiter length */
804     MsgWaiterQueue->MinimumWaiterLength = (ULONG)-1;
805 
806     /* Init the semaphore and event used for counting and signaling available IRPs */
807     KeInitializeSemaphore(&MsgWaiterQueue->Semaphore, 0, MAXLONG);
808     KeInitializeEvent(&MsgWaiterQueue->Event, NotificationEvent, FALSE);
809 
810     return STATUS_SUCCESS;
811 }
812 
813 static
814 PPORT_CCB
815 CreatePortCCB(_In_ PFLT_PORT_OBJECT PortObject)
816 {
817     PPORT_CCB PortCCB;
818 
819     /* Allocate a CCB struct to hold the client port object info */
820     PortCCB = ExAllocatePoolWithTag(NonPagedPool, sizeof(PORT_CCB), FM_TAG_CCB);
821     if (PortCCB)
822     {
823         /* Initialize the structure */
824         PortCCB->Port = PortObject;
825         PortCCB->ReplyWaiterList.mCount = 0;
826         ExInitializeFastMutex(&PortCCB->ReplyWaiterList.mLock);
827         KeInitializeEvent(&PortCCB->ReplyWaiterList.mLock.Event, SynchronizationEvent, 0);
828     }
829 
830     return PortCCB;
831 }
832