1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
/*
* PROJECT:         Filesystem Filter Manager
* LICENSE:         GPL - See COPYING in the top level directory
* FILE:            drivers/filters/fltmgr/Filter.c
* PURPOSE:         Handles registration of mini filters
* PROGRAMMERS:     Ged Murphy (gedmurphy@reactos.org)
*/

/* INCLUDES ******************************************************************/

#include "fltmgr.h"
#include "fltmgrint.h"
#include "Registry.h"

#define NDEBUG
#include <debug.h>


/* DATA *********************************************************************/

#define SERVICES_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
#define MAX_KEY_LENGTH  0x200

LIST_ENTRY FilterList;
ERESOURCE FilterListLock;

NTSTATUS
FltpStartingToDrainObject(
    _Inout_ PFLT_OBJECT Object
);

VOID
FltpMiniFilterDriverUnload(
);

NTSTATUS
FltpAttachFrame(
    _In_ PUNICODE_STRING Altitude,
    _Inout_ PFLTP_FRAME *Frame
);

static
NTSTATUS
GetFilterAltitude(
    _In_ PFLT_FILTER Filter,
    _Inout_ PUNICODE_STRING AltitudeString
);

static
NTSTATUS
GetFilterFrame(
    _In_ PFLT_FILTER Filter,
    _In_ PUNICODE_STRING Altitude,
    _Out_ PFLTP_FRAME *Frame
);


/* EXPORTED FUNCTIONS ******************************************************/

NTSTATUS
NTAPI
FltLoadFilter(_In_ PCUNICODE_STRING FilterName)
{
    UNICODE_STRING DriverServiceName;
    UNICODE_STRING ServicesKey;
    CHAR Buffer[MAX_KEY_LENGTH];

    /* Setup the base services key */
    RtlInitUnicodeString(&ServicesKey, SERVICES_KEY);

    /* Initialize the string data */
    DriverServiceName.Length = 0;
    DriverServiceName.Buffer = (PWCH)Buffer;
    DriverServiceName.MaximumLength = MAX_KEY_LENGTH;

    /* Create the full service key for this filter */
    RtlCopyUnicodeString(&DriverServiceName, &ServicesKey);
    RtlAppendUnicodeStringToString(&DriverServiceName, FilterName);

    /* Ask the kernel to load it for us */
    return ZwLoadDriver(&DriverServiceName);
}

NTSTATUS
NTAPI
FltUnloadFilter(_In_ PCUNICODE_STRING FilterName)
{
    //
    //FIXME: This is a temp hack, it needs properly implementing
    //

    UNICODE_STRING DriverServiceName;
    UNICODE_STRING ServicesKey;
    CHAR Buffer[MAX_KEY_LENGTH];

    /* Setup the base services key */
    RtlInitUnicodeString(&ServicesKey, SERVICES_KEY);

    /* Initialize the string data */
    DriverServiceName.Length = 0;
    DriverServiceName.Buffer = (PWCH)Buffer;
    DriverServiceName.MaximumLength = MAX_KEY_LENGTH;

    /* Create the full service key for this filter */
    RtlCopyUnicodeString(&DriverServiceName, &ServicesKey);
    RtlAppendUnicodeStringToString(&DriverServiceName, FilterName);
    return ZwUnloadDriver(&DriverServiceName);
}

NTSTATUS
NTAPI
FltRegisterFilter(_In_ PDRIVER_OBJECT DriverObject,
                  _In_ const FLT_REGISTRATION *Registration,
                  _Out_ PFLT_FILTER *RetFilter)
{
    PFLT_OPERATION_REGISTRATION Callbacks;
    PFLT_FILTER Filter;
    PFLTP_FRAME Frame;
    ULONG CallbackBufferSize;
    ULONG FilterBufferSize;
    ULONG Count = 0;
    PCHAR Ptr;
    NTSTATUS Status;

    *RetFilter = NULL;

    /* Make sure we're targeting the correct major revision */
    if ((Registration->Version & 0xFF00) != FLT_MAJOR_VERSION)
    {
        return STATUS_INVALID_PARAMETER;
    }

    /* Make sure our namespace callbacks are valid */
    if ((!Registration->GenerateFileNameCallback && Registration->NormalizeNameComponentCallback) ||
        (!Registration->NormalizeNameComponentCallback && Registration->NormalizeContextCleanupCallback))
    {
        return STATUS_INVALID_PARAMETER;
    }

    /* Count the number of operations that were requested */
    Callbacks = (PFLT_OPERATION_REGISTRATION)Registration->OperationRegistration;
    while (Callbacks)
    {
        Count++;

        /* Bail when we find the last one */
        if (Callbacks->MajorFunction == IRP_MJ_OPERATION_END)
            break;

        /* Move to the next item */
        Callbacks++;
    }

    /* Calculate the buffer sizes */
    CallbackBufferSize = Count * sizeof(FLT_OPERATION_REGISTRATION);
    FilterBufferSize = sizeof(FLT_FILTER) +
                       CallbackBufferSize +
                       DriverObject->DriverExtension->ServiceKeyName.Length;

    /* Allocate a buffer to hold our filter data */
    Filter = ExAllocatePoolWithTag(NonPagedPool,
                                   FilterBufferSize,
                                   FM_TAG_FILTER);
    if (Filter == NULL) return STATUS_INSUFFICIENT_RESOURCES;
    RtlZeroMemory(Filter, FilterBufferSize);

    /* Find the end of the fixed struct */
    Ptr = (PCHAR)(Filter + 1);

    /* Store a copy of the driver object of this filter */
    Filter->DriverObject = DriverObject;

    /* Initialize the base object data */
    Filter->Base.Flags = FLT_OBFL_TYPE_FILTER;
    Filter->Base.PointerCount = 1;
    FltpExInitializeRundownProtection(&Filter->Base.RundownRef);
    FltObjectReference(&Filter->Base);

    /* Set the callback addresses */
    Filter->FilterUnload = Registration->FilterUnloadCallback;
    Filter->InstanceSetup = Registration->InstanceSetupCallback;
    Filter->InstanceQueryTeardown = Registration->InstanceQueryTeardownCallback;
    Filter->InstanceTeardownStart = Registration->InstanceTeardownStartCallback;
    Filter->InstanceTeardownComplete = Registration->InstanceTeardownCompleteCallback;
    Filter->GenerateFileName = Registration->GenerateFileNameCallback;
    Filter->NormalizeNameComponent = Registration->NormalizeNameComponentCallback;
    Filter->NormalizeContextCleanup = Registration->NormalizeContextCleanupCallback;

    /* Initialize the instance list */
    ExInitializeResourceLite(&Filter->InstanceList.rLock);
    InitializeListHead(&Filter->InstanceList.rList);
    Filter->InstanceList.rCount = 0;

    ExInitializeFastMutex(&Filter->ActiveOpens.mLock);
    InitializeListHead(&Filter->ActiveOpens.mList);
    Filter->ActiveOpens.mCount = 0;

    ExInitializeFastMutex(&Filter->ConnectionList.mLock);
    InitializeListHead(&Filter->ConnectionList.mList);
    Filter->ConnectionList.mCount = 0;

    /* Initialize the usermode port list */
    ExInitializeFastMutex(&Filter->PortList.mLock);
    InitializeListHead(&Filter->PortList.mList);
    Filter->PortList.mCount = 0;

    /* We got this far, assume success from here */
    Status = STATUS_SUCCESS;<--- Status is assigned

    /* Check if the caller requested any context data */
    if (Registration->ContextRegistration)
    {
        /* Register the contexts for this filter */
        Status = FltpRegisterContexts(Filter, Registration->ContextRegistration);
        if (NT_SUCCESS(Status))
        {
            goto Quit;
        }
    }

    /* Check if the caller is registering any callbacks */
    if (Registration->OperationRegistration)
    {
        /* The callback data comes after the fixed struct */
        Filter->Operations = (PFLT_OPERATION_REGISTRATION)Ptr;
        Ptr += (Count * sizeof(FLT_OPERATION_REGISTRATION));

        /* Tag the operation data onto the end of the filter data */
        RtlCopyMemory(Filter->Operations, Registration->OperationRegistration, CallbackBufferSize);

        /* walk through the requested callbacks */
        for (Callbacks = Filter->Operations;
             Callbacks->MajorFunction != IRP_MJ_OPERATION_END;
             Callbacks++)
        {
            // http://fsfilters.blogspot.co.uk/2011/03/how-file-system-filters-attach-to_17.html
            /* Check if this is an attach to a volume */
            if (Callbacks->MajorFunction == IRP_MJ_VOLUME_MOUNT)
            {
                Filter->PreVolumeMount = Callbacks->PreOperation;
                Filter->PostVolumeMount = Callbacks->PostOperation;
            }
            else if (Callbacks->MajorFunction == IRP_MJ_SHUTDOWN)
            {
                Callbacks->PostOperation = NULL;
            }
        }
    }

    /* Add the filter name buffer onto the end of the data and fill in the string */
    Filter->Name.Length = 0;
    Filter->Name.MaximumLength = DriverObject->DriverExtension->ServiceKeyName.Length;
    Filter->Name.Buffer = (PWCH)Ptr;
    RtlCopyUnicodeString(&Filter->Name, &DriverObject->DriverExtension->ServiceKeyName);

    /* Lookup the altitude of the mini-filter */
    Status = GetFilterAltitude(Filter, &Filter->DefaultAltitude);<--- Status is overwritten
    if (!NT_SUCCESS(Status))
    {
        goto Quit;
    }

    /* Lookup the filter frame */
    Status = GetFilterFrame(Filter, &Filter->DefaultAltitude, &Frame);
    if (Status == STATUS_NOT_FOUND)
    {
        /* Store the frame this mini-filter's main struct */
        Filter->Frame = Frame;

        Status = FltpAttachFrame(&Filter->DefaultAltitude, &Frame);
    }

    if (!NT_SUCCESS(Status))
    {
        goto Quit;
    }

    //
    // - Slot the filter into the correct altitude location
    // - More stuff??
    //

    /* Store any existing driver unload routine before we make any changes */
    Filter->OldDriverUnload = (PFLT_FILTER_UNLOAD_CALLBACK)DriverObject->DriverUnload;

    /* Check we opted not to have an unload routine, or if we want to stop the driver from being unloaded */
    if (Registration->FilterUnloadCallback && !FlagOn(Filter->Flags, FLTFL_REGISTRATION_DO_NOT_SUPPORT_SERVICE_STOP))
    {
        DriverObject->DriverUnload = (PDRIVER_UNLOAD)FltpMiniFilterDriverUnload;
    }
    else
    {
        DriverObject->DriverUnload = (PDRIVER_UNLOAD)NULL;
    }


Quit:

    if (NT_SUCCESS(Status))
    {
        DPRINT1("Loaded FS mini-filter %wZ\n", &DriverObject->DriverExtension->ServiceKeyName);
        *RetFilter = Filter;
    }
    else
    {
        DPRINT1("Failed to load FS mini-filter %wZ : 0x%X\n", &DriverObject->DriverExtension->ServiceKeyName, Status);

        // Add cleanup for context resources

        ExDeleteResourceLite(&Filter->InstanceList.rLock);
        ExFreePoolWithTag(Filter, FM_TAG_FILTER);
    }

    return Status;
}

VOID
FLTAPI
FltUnregisterFilter(_In_ PFLT_FILTER Filter)
{
    PFLT_INSTANCE Instance;
    PLIST_ENTRY CurrentEntry;
    NTSTATUS Status;

    /* Set the draining flag */
    Status = FltpStartingToDrainObject(&Filter->Base);
    if (!NT_SUCCESS(Status))
    {
        /* Someone already unregistered us, just remove our ref and bail */
        FltObjectDereference(&Filter->Base);
        return;
    }

    /* Lock the instance list */
    KeEnterCriticalRegion();
    ExAcquireResourceSharedLite(&Filter->InstanceList.rLock, TRUE);

    /* Set the first entry in the list */
    CurrentEntry = Filter->InstanceList.rList.Flink;

    /* Free all instances referenced by the filter */
    while (CurrentEntry != &Filter->InstanceList.rList)
    {
        /* Get the record pointer */
        Instance = CONTAINING_RECORD(CurrentEntry, FLT_INSTANCE, FilterLink);

        // FIXME: implement
        (void)Instance;

        /* Reset the pointer and move to next entry */
        Instance = NULL;
        CurrentEntry = CurrentEntry->Flink;
    }

    /* We're done with instances now */
    ExReleaseResourceLite(&Filter->InstanceList.rLock);
    KeLeaveCriticalRegion();

    /* Remove the reference from the base object */
    FltObjectDereference(&Filter->Base);

    /* Wait until we're sure nothing is using the filter */
    FltpObjectRundownWait(&Filter->Base.RundownRef);

    /* Delete the instance list lock */
    ExDeleteResourceLite(&Filter->InstanceList.rLock);

    /* We're finished cleaning up now */
    FltpExRundownCompleted(&Filter->Base.RundownRef);

    /* Hand the memory back */
    ExFreePoolWithTag(Filter, FM_TAG_FILTER);
}

NTSTATUS
NTAPI
FltStartFiltering(_In_ PFLT_FILTER Filter)
{
    NTSTATUS Status;

    /* Grab a ref to the filter */
    Status = FltObjectReference(&Filter->Base);
    if (NT_SUCCESS(Status))
    {
        /* Make sure we aren't already starting up */
        if (!(Filter->Flags & FLTFL_FILTERING_INITIATED))
        {
            // Startup
        }
        else
        {
            Status = STATUS_INVALID_PARAMETER;
        }

        FltObjectDereference(&Filter->Base);
    }

    return Status;
}

NTSTATUS
NTAPI
FltGetFilterFromName(_In_ PCUNICODE_STRING FilterName,
                     _Out_ PFLT_FILTER *RetFilter)
{
   UNIMPLEMENTED;
    UNREFERENCED_PARAMETER(FilterName);
    *RetFilter = NULL;
    return STATUS_NOT_IMPLEMENTED;
}


/* INTERNAL FUNCTIONS ******************************************************/

NTSTATUS
FltpStartingToDrainObject(_Inout_ PFLT_OBJECT Object)
{
    /*
     * Set the draining flag for the filter. This let's us force
     * a post op callback for minifilters currently awaiting one.
     */
    if (InterlockedOr((PLONG)&Object->Flags, FLT_OBFL_DRAINING) & 1)
    {
        /* We've been called once, we're already being deleted */
        return STATUS_FLT_DELETING_OBJECT;
    }

    return STATUS_SUCCESS;
}

VOID
FltpMiniFilterDriverUnload()
{
    __debugbreak();
}


NTSTATUS
FltpAttachFrame(
    _In_ PUNICODE_STRING Altitude,
    _Inout_ PFLTP_FRAME *Frame)
{
    UNIMPLEMENTED;
    UNREFERENCED_PARAMETER(Altitude);
    *Frame = NULL;
    return STATUS_SUCCESS;
}

/* PRIVATE FUNCTIONS ******************************************************/

static
NTSTATUS
GetFilterAltitude(_In_ PFLT_FILTER Filter,
                  _Inout_ PUNICODE_STRING AltitudeString)
{
    UNICODE_STRING InstancesKey = RTL_CONSTANT_STRING(L"Instances");
    UNICODE_STRING DefaultInstance = RTL_CONSTANT_STRING(L"DefaultInstance");
    UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"Altitude");
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING FilterInstancePath;
    ULONG BytesRequired;
    HANDLE InstHandle = NULL;
    HANDLE RootHandle;
    PWCH InstBuffer = NULL;
    PWCH AltBuffer = NULL;
    NTSTATUS Status;

    /* Get a handle to the instances key in the filter's services key */
    Status = FltpOpenFilterServicesKey(Filter,
                                       KEY_QUERY_VALUE,
                                       &InstancesKey,
                                       &RootHandle);
    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    /* Read the size 'default instances' string value */
    Status = FltpReadRegistryValue(RootHandle,
                                   &DefaultInstance,
                                   REG_SZ,
                                   NULL,
                                   0,
                                   &BytesRequired);

    /* We should get a buffer too small error */
    if (Status == STATUS_BUFFER_TOO_SMALL)
    {
        /* Allocate the buffer we need to hold the string */
        InstBuffer = ExAllocatePoolWithTag(PagedPool, BytesRequired, FM_TAG_UNICODE_STRING);
        if (InstBuffer == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Quit;
        }

        /* Now read the string value */
        Status = FltpReadRegistryValue(RootHandle,
                                       &DefaultInstance,
                                       REG_SZ,
                                       InstBuffer,
                                       BytesRequired,
                                       &BytesRequired);
    }

    if (!NT_SUCCESS(Status))
    {
        goto Quit;
    }

    /* Convert the string to a unicode_string */
    RtlInitUnicodeString(&FilterInstancePath, InstBuffer);

    /* Setup the attributes using the root key handle */
    InitializeObjectAttributes(&ObjectAttributes,
                               &FilterInstancePath,
                               OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
                               RootHandle,
                               NULL);

    /* Now open the key name which was stored in the default instance */
    Status = ZwOpenKey(&InstHandle, KEY_QUERY_VALUE, &ObjectAttributes);
    if (NT_SUCCESS(Status))
    {
        /* Get the size of the buffer that holds the altitude */
        Status = FltpReadRegistryValue(InstHandle,
                                       &Altitude,
                                       REG_SZ,
                                       NULL,
                                       0,
                                       &BytesRequired);
        if (Status == STATUS_BUFFER_TOO_SMALL)
        {
            /* Allocate the required buffer */
            AltBuffer = ExAllocatePoolWithTag(PagedPool, BytesRequired, FM_TAG_UNICODE_STRING);
            if (AltBuffer == NULL)
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto Quit;
            }

            /* And now finally read in the actual altitude string */
            Status = FltpReadRegistryValue(InstHandle,
                                           &Altitude,
                                           REG_SZ,
                                           AltBuffer,
                                           BytesRequired,
                                           &BytesRequired);
            if (NT_SUCCESS(Status))
            {
                /* We made it, setup the return buffer */
                AltitudeString->Length = BytesRequired;
                AltitudeString->MaximumLength = BytesRequired;
                AltitudeString->Buffer = AltBuffer;
            }
        }
    }

Quit:
    if (!NT_SUCCESS(Status))
    {
        if (AltBuffer)
        {
            ExFreePoolWithTag(AltBuffer, FM_TAG_UNICODE_STRING);
        }
    }

    if (InstBuffer)
    {
        ExFreePoolWithTag(InstBuffer, FM_TAG_UNICODE_STRING);
    }

    if (InstHandle)
    {
        ZwClose(InstHandle);
    }
    ZwClose(RootHandle);

    return Status;
}

static
NTSTATUS
GetFilterFrame(_In_ PFLT_FILTER Filter,
               _In_ PUNICODE_STRING Altitude,
               _Out_ PFLTP_FRAME *Frame)
{
    UNIMPLEMENTED;
    UNREFERENCED_PARAMETER(Filter);
    UNREFERENCED_PARAMETER(Altitude);

    //
    // Try to find a frame from our existing filter list (see FilterList)
    // If none exists, create a new frame, add it and return it
    //

    *Frame = NULL;
    return STATUS_SUCCESS;
}