/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Kernel Streaming * FILE: drivers/ksfilter/ks/allocators.c * PURPOSE: KS Allocator functions * PROGRAMMER: Johannes Anderwald */ #include "precomp.h" #define NDEBUG #include typedef enum { ALLOCATOR_NPAGED_LOOKASIDE, ALLOCATOR_PAGED_LOOKASIDE, ALLOCATOR_CUSTOM }ALLOCATOR_TYPE; typedef enum { ALLOCATOR_DEVICE_CONTROL, ALLOCATOR_DEVICE_CLOSE, ALLOCATOR_ALLOCATE, ALLOCATOR_FREE }ALLOC_REQUEST; typedef PVOID (*PFNKSPAGEDPOOLALLOCATE)(IN PPAGED_LOOKASIDE_LIST Lookaside); typedef PVOID (*PFNKSNPAGEDPOOLALLOCATE)(IN PNPAGED_LOOKASIDE_LIST Lookaside); typedef VOID (*PFNKSPAGEDPOOLFREE)(IN PPAGED_LOOKASIDE_LIST Lookaside, IN PVOID Entry); typedef VOID (*PFNKSNPAGEDPOOLFREE)(IN PNPAGED_LOOKASIDE_LIST Lookaside, IN PVOID Entry); typedef VOID (NTAPI *PFNKSNPAGEDPOOLDELETE)(IN PNPAGED_LOOKASIDE_LIST Lookaside); typedef VOID (NTAPI *PFNKSPAGEDPOOLDELETE)(IN PPAGED_LOOKASIDE_LIST Lookaside); typedef struct { IKsAllocatorVtbl *lpVtbl; LONG ref; PKSIOBJECT_HEADER Header; ALLOCATOR_TYPE Type; KSSTREAMALLOCATOR_STATUS Status; union { NPAGED_LOOKASIDE_LIST NPagedList; PAGED_LOOKASIDE_LIST PagedList; PVOID CustomList; }u; union { PFNKSDEFAULTALLOCATE DefaultAllocate; PFNKSPAGEDPOOLALLOCATE PagedPool; PFNKSNPAGEDPOOLALLOCATE NPagedPool; }Allocate; union { PFNKSDEFAULTFREE DefaultFree; PFNKSPAGEDPOOLFREE PagedPool; PFNKSNPAGEDPOOLFREE NPagedPool; }Free; union { PFNKSDELETEALLOCATOR DefaultDelete; PFNKSNPAGEDPOOLDELETE NPagedPool; PFNKSPAGEDPOOLDELETE PagedPool; }Delete; }ALLOCATOR, *PALLOCATOR; /* use KSNAME_Allocator for IID_IKsAllocator */ const GUID IID_IKsAllocator = {0x642F5D00L, 0x4791, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; const GUID KSPROPSETID_StreamAllocator = {0x0cf6e4342, 0xec87, 0x11cf, {0xa1, 0x30, 0x00, 0x20, 0xaf, 0xd1, 0x56, 0xe4}}; NTSTATUS NTAPI IKsAllocator_Allocate( IN PFILE_OBJECT FileObject, PVOID *Frame); VOID NTAPI IKsAllocator_FreeFrame( IN PFILE_OBJECT FileObject, PVOID Frame); NTSTATUS NTAPI IKsAllocator_fnQueryInterface( IKsAllocator * iface, IN REFIID refiid, OUT PVOID* Output) { PALLOCATOR This = (PALLOCATOR)CONTAINING_RECORD(iface, ALLOCATOR, lpVtbl); if (IsEqualGUIDAligned(refiid, &IID_IUnknown) || IsEqualGUIDAligned(refiid, &IID_IKsAllocator)) { *Output = &This->lpVtbl; _InterlockedIncrement(&This->ref); return STATUS_SUCCESS; } return STATUS_UNSUCCESSFUL; } ULONG NTAPI IKsAllocator_fnAddRef( IKsAllocator * iface) { PALLOCATOR This = (PALLOCATOR)CONTAINING_RECORD(iface, ALLOCATOR, lpVtbl); return InterlockedIncrement(&This->ref); } ULONG NTAPI IKsAllocator_fnRelease( IKsAllocator * iface) { PALLOCATOR This = (PALLOCATOR)CONTAINING_RECORD(iface, ALLOCATOR, lpVtbl); InterlockedDecrement(&This->ref); if (This->ref == 0) { FreeItem(This); return 0; } /* Return new reference count */ return This->ref; } NTSTATUS NTAPI IKsAllocator_fnDeviceIoControl( IKsAllocator *iface, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PALLOCATOR This = (PALLOCATOR)CONTAINING_RECORD(iface, ALLOCATOR, lpVtbl); PIO_STACK_LOCATION IoStack; PKSSTREAMALLOCATOR_FUNCTIONTABLE FunctionTable; PKSSTREAMALLOCATOR_STATUS State; PKSPROPERTY Property; /* FIXME locks */ /* get current irp stack */ IoStack = IoGetCurrentIrpStackLocation(Irp); if (IoStack->Parameters.DeviceIoControl.IoControlCode != IOCTL_KS_PROPERTY) { /* only KSPROPERTY requests are supported */ UNIMPLEMENTED; /* complete and forget irps */ Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NOT_IMPLEMENTED; } if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSPROPERTY)) { /* invalid request */ Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } /* check the request */ Property = (PKSPROPERTY)IoStack->Parameters.DeviceIoControl.Type3InputBuffer; if (IsEqualGUIDAligned(&Property->Set, &KSPROPSETID_StreamAllocator)) { if (Property->Id == KSPROPERTY_STREAMALLOCATOR_FUNCTIONTABLE) { if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KSSTREAMALLOCATOR_FUNCTIONTABLE)) { /* buffer too small */ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(KSSTREAMALLOCATOR_FUNCTIONTABLE); /* complete and forget irp */ CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_BUFFER_TOO_SMALL; } if (!(Property->Flags & KSPROPERTY_TYPE_GET)) { /* only support retrieving the property */ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; /* complete and forget irp */ CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_UNSUCCESSFUL; } /* get output buffer */ FunctionTable = (PKSSTREAMALLOCATOR_FUNCTIONTABLE)Irp->UserBuffer; FunctionTable->AllocateFrame = IKsAllocator_Allocate; FunctionTable->FreeFrame = IKsAllocator_FreeFrame; /* save result */ Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(KSSTREAMALLOCATOR_FUNCTIONTABLE); /* complete request */ CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } else if (Property->Id == KSPROPERTY_STREAMALLOCATOR_STATUS) { if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KSPROPERTY_STREAMALLOCATOR_STATUS)) { /* buffer too small */ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(KSPROPERTY_STREAMALLOCATOR_STATUS); /* complete and forget irp */ CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_BUFFER_TOO_SMALL; } if (!(Property->Flags & KSPROPERTY_TYPE_GET)) { /* only support retrieving the property */ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; /* complete and forget irp */ CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_UNSUCCESSFUL; } /* get output buffer */ State = (PKSSTREAMALLOCATOR_STATUS)Irp->UserBuffer; /* copy allocator status */ RtlMoveMemory(State, &This->Status, sizeof(KSSTREAMALLOCATOR_STATUS)); /* save result */ Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(KSSTREAMALLOCATOR_STATUS); /* complete request */ CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } } /* unhandled request */ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; CompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NOT_SUPPORTED; } NTSTATUS NTAPI IKsAllocator_fnClose( IKsAllocator *iface) { PALLOCATOR This = (PALLOCATOR)CONTAINING_RECORD(iface, ALLOCATOR, lpVtbl); /* FIXME locks */ /* now close allocator */ if (This->Type == ALLOCATOR_CUSTOM) { This->Delete.DefaultDelete(This->u.CustomList); } else if (This->Type == ALLOCATOR_NPAGED_LOOKASIDE) { This->Delete.NPagedPool(&This->u.NPagedList); } else if (This->Type == ALLOCATOR_PAGED_LOOKASIDE) { This->Delete.PagedPool(&This->u.PagedList); } /* free object header */ KsFreeObjectHeader(&This->Header); return STATUS_SUCCESS; } NTSTATUS NTAPI IKsAllocator_fnAllocateFrame( IKsAllocator *iface, IN PVOID * OutFrame) { PVOID Frame = NULL; PALLOCATOR This = (PALLOCATOR)CONTAINING_RECORD(iface, ALLOCATOR, lpVtbl); /* FIXME locks */ /* now allocate frame */ if (This->Type == ALLOCATOR_CUSTOM) { Frame = This->Allocate.DefaultAllocate(This->u.CustomList); } else if (This->Type == ALLOCATOR_NPAGED_LOOKASIDE) { Frame = This->Allocate.NPagedPool(&This->u.NPagedList); } else if (This->Type == ALLOCATOR_PAGED_LOOKASIDE) { Frame = This->Allocate.PagedPool(&This->u.PagedList); } if (Frame) { *OutFrame = Frame; InterlockedIncrement((PLONG)&This->Status.AllocatedFrames); return STATUS_SUCCESS; } return STATUS_UNSUCCESSFUL; } VOID NTAPI IKsAllocator_fnFreeFrame( IKsAllocator *iface, IN PVOID Frame) { PALLOCATOR This = (PALLOCATOR)CONTAINING_RECORD(iface, ALLOCATOR, lpVtbl); /* now allocate frame */ if (This->Type == ALLOCATOR_CUSTOM) { This->Free.DefaultFree(This->u.CustomList, Frame); } else if (This->Type == ALLOCATOR_NPAGED_LOOKASIDE) { This->Free.NPagedPool(&This->u.NPagedList, Frame); } else if (This->Type == ALLOCATOR_PAGED_LOOKASIDE) { This->Free.PagedPool(&This->u.PagedList, Frame); } } static IKsAllocatorVtbl vt_IKsAllocator = { IKsAllocator_fnQueryInterface, IKsAllocator_fnAddRef, IKsAllocator_fnRelease, IKsAllocator_fnDeviceIoControl, IKsAllocator_fnClose, IKsAllocator_fnAllocateFrame, IKsAllocator_fnFreeFrame }; /* @implemented */ KSDDKAPI NTSTATUS NTAPI KsCreateAllocator( IN HANDLE ConnectionHandle, IN PKSALLOCATOR_FRAMING AllocatorFraming, OUT PHANDLE AllocatorHandle) { return KspCreateObjectType(ConnectionHandle, KSSTRING_Allocator, (PVOID)AllocatorFraming, sizeof(KSALLOCATOR_FRAMING), GENERIC_READ, AllocatorHandle); } /* @implemented */ KSDDKAPI NTSTATUS NTAPI KsCreateDefaultAllocator( IN PIRP Irp) { return KsCreateDefaultAllocatorEx(Irp, NULL, NULL, NULL, NULL, NULL); } /* @implemented */ KSDDKAPI NTSTATUS NTAPI KsValidateAllocatorCreateRequest( IN PIRP Irp, OUT PKSALLOCATOR_FRAMING* OutAllocatorFraming) { PKSALLOCATOR_FRAMING AllocatorFraming; ULONG Size; NTSTATUS Status; ULONG SupportedFlags; /* set minimum request size */ Size = sizeof(KSALLOCATOR_FRAMING); Status = KspCopyCreateRequest(Irp, KSSTRING_Allocator, &Size, (PVOID*)&AllocatorFraming); if (!NT_SUCCESS(Status)) return Status; /* allowed supported flags */ SupportedFlags = (KSALLOCATOR_OPTIONF_COMPATIBLE | KSALLOCATOR_OPTIONF_SYSTEM_MEMORY | KSALLOCATOR_REQUIREMENTF_INPLACE_MODIFIER | KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | KSALLOCATOR_REQUIREMENTF_FRAME_INTEGRITY | KSALLOCATOR_REQUIREMENTF_MUST_ALLOCATE); if (!AllocatorFraming->FrameSize || (AllocatorFraming->OptionsFlags & (~SupportedFlags))) { FreeItem(AllocatorFraming); return STATUS_INVALID_PARAMETER; } /* store result */ *OutAllocatorFraming = AllocatorFraming; return Status; } NTSTATUS IKsAllocator_DispatchRequest( IN PDEVICE_OBJECT DeviceObject, IN PFILE_OBJECT FileObject, IN PIRP Irp, IN PVOID Frame, IN ALLOC_REQUEST Request) { PKSIOBJECT_HEADER Header; NTSTATUS Status; IKsAllocator * Allocator; /* sanity check */ ASSERT(FileObject); /* get object header */ Header = (PKSIOBJECT_HEADER)FileObject->FsContext2; /* get real allocator */ Status = Header->Unknown->lpVtbl->QueryInterface(Header->Unknown, &IID_IKsAllocator, (PVOID*)&Allocator); if (!NT_SUCCESS(Status)) { /* misbehaving object */ return STATUS_UNSUCCESSFUL; } if (Request == ALLOCATOR_DEVICE_CONTROL) { /* dispatch request allocator */ Status = Allocator->lpVtbl->DispatchDeviceIoControl(Allocator, DeviceObject, Irp); } else if (Request == ALLOCATOR_DEVICE_CLOSE) { /* delete allocator */ Status = Allocator->lpVtbl->Close(Allocator); } else if (Request == ALLOCATOR_ALLOCATE) { /* allocate frame */ Status = Allocator->lpVtbl->AllocateFrame(Allocator, (PVOID*)Frame); }else if (Request == ALLOCATOR_FREE) { /* allocate frame */ Allocator->lpVtbl->FreeFrame(Allocator, Frame); Status = STATUS_SUCCESS; } /* release interface */ Allocator->lpVtbl->Release(Allocator); return Status; } NTSTATUS NTAPI IKsAllocator_DispatchDeviceIoControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION IoStack; NTSTATUS Status; /* get current irp stack */ IoStack = IoGetCurrentIrpStackLocation(Irp); /* dispatch request */ Status = IKsAllocator_DispatchRequest(DeviceObject, IoStack->FileObject, Irp, NULL, ALLOCATOR_DEVICE_CONTROL); /* complete request */ Irp->IoStatus.Status = Status; CompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS NTAPI IKsAllocator_DispatchClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION IoStack; NTSTATUS Status; /* get current irp stack */ IoStack = IoGetCurrentIrpStackLocation(Irp); /* dispatch request */ Status = IKsAllocator_DispatchRequest(DeviceObject, IoStack->FileObject, Irp, NULL, ALLOCATOR_DEVICE_CLOSE); /* complete request */ Irp->IoStatus.Status = Status; CompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS NTAPI IKsAllocator_Allocate( IN PFILE_OBJECT FileObject, PVOID *Frame) { NTSTATUS Status; /* dispatch request */ Status = IKsAllocator_DispatchRequest(NULL, FileObject, NULL, (PVOID)Frame, ALLOCATOR_ALLOCATE); return Status; } VOID NTAPI IKsAllocator_FreeFrame( IN PFILE_OBJECT FileObject, PVOID Frame) { /* dispatch request */ IKsAllocator_DispatchRequest(NULL, FileObject, NULL, Frame, ALLOCATOR_FREE); } static KSDISPATCH_TABLE DispatchTable = { IKsAllocator_DispatchDeviceIoControl, KsDispatchInvalidDeviceRequest, KsDispatchInvalidDeviceRequest, KsDispatchInvalidDeviceRequest, IKsAllocator_DispatchClose, KsDispatchQuerySecurity, KsDispatchSetSecurity, KsDispatchFastIoDeviceControlFailure, KsDispatchFastReadFailure, KsDispatchFastReadFailure, }; /* @implemented */ KSDDKAPI NTSTATUS NTAPI KsCreateDefaultAllocatorEx( IN PIRP Irp, IN PVOID InitializeContext OPTIONAL, IN PFNKSDEFAULTALLOCATE DefaultAllocate OPTIONAL, IN PFNKSDEFAULTFREE DefaultFree OPTIONAL, IN PFNKSINITIALIZEALLOCATOR InitializeAllocator OPTIONAL, IN PFNKSDELETEALLOCATOR DeleteAllocator OPTIONAL) { NTSTATUS Status; PKSALLOCATOR_FRAMING AllocatorFraming; PALLOCATOR Allocator; PVOID Ctx; /* first validate connect request */ Status = KsValidateAllocatorCreateRequest(Irp, &AllocatorFraming); if (!NT_SUCCESS(Status)) return STATUS_INVALID_PARAMETER; /* check the valid file alignment */ if (AllocatorFraming->FileAlignment > (PAGE_SIZE-1)) { FreeItem(AllocatorFraming); return STATUS_INVALID_PARAMETER; } /* allocate allocator struct */ Allocator = AllocateItem(NonPagedPool, sizeof(ALLOCATOR)); if (!Allocator) { FreeItem(AllocatorFraming); return STATUS_INSUFFICIENT_RESOURCES; } /* allocate object header */ Status = KsAllocateObjectHeader((KSOBJECT_HEADER*)&Allocator->Header, 0, NULL, Irp, &DispatchTable); if (!NT_SUCCESS(Status)) { FreeItem(AllocatorFraming); FreeItem(Allocator); return Status; } /* set allocator type in object header */ Allocator->lpVtbl = &vt_IKsAllocator; Allocator->Header->Unknown = (PUNKNOWN)&Allocator->lpVtbl; Allocator->ref = 1; if (DefaultAllocate) { /* use external allocator */ Allocator->Type = ALLOCATOR_CUSTOM; Allocator->Allocate.DefaultAllocate = DefaultAllocate; Allocator->Free.DefaultFree = DefaultFree; Allocator->Delete.DefaultDelete = DeleteAllocator; Ctx = InitializeAllocator(InitializeContext, AllocatorFraming, &Allocator->u.CustomList); /* check for success */ if (!Ctx) { KsFreeObjectHeader(Allocator->Header); FreeItem(Allocator); return Status; } } else if (AllocatorFraming->PoolType == NonPagedPool) { /* use non-paged pool allocator */ Allocator->Type = ALLOCATOR_NPAGED_LOOKASIDE; Allocator->Allocate.NPagedPool = ExAllocateFromNPagedLookasideList; Allocator->Free.NPagedPool = ExFreeToNPagedLookasideList; Allocator->Delete.NPagedPool = ExDeleteNPagedLookasideList; ExInitializeNPagedLookasideList(&Allocator->u.NPagedList, NULL, NULL, 0, AllocatorFraming->FrameSize, 0, 0); } else if (AllocatorFraming->PoolType == PagedPool) { /* use paged pool allocator */ Allocator->Allocate.PagedPool = ExAllocateFromPagedLookasideList; Allocator->Free.PagedPool = ExFreeToPagedLookasideList; Allocator->Delete.PagedPool = ExDeletePagedLookasideList; Allocator->Type = ALLOCATOR_PAGED_LOOKASIDE; ExInitializePagedLookasideList(&Allocator->u.PagedList, NULL, NULL, 0, AllocatorFraming->FrameSize, 0, 0); } /* backup allocator framing */ RtlMoveMemory(&Allocator->Status.Framing, AllocatorFraming, sizeof(KSALLOCATOR_FRAMING)); FreeItem(AllocatorFraming); return Status; } /* @implemented */ KSDDKAPI NTSTATUS NTAPI KsValidateAllocatorFramingEx( IN PKSALLOCATOR_FRAMING_EX Framing, IN ULONG BufferSize, IN const KSALLOCATOR_FRAMING_EX* PinFraming) { if (BufferSize < sizeof(KSALLOCATOR_FRAMING_EX)) return STATUS_INVALID_DEVICE_REQUEST; /* verify framing */ if ((Framing->FramingItem[0].Flags & KSALLOCATOR_FLAG_PARTIAL_READ_SUPPORT) && Framing->OutputCompression.RatioNumerator != MAXULONG && Framing->OutputCompression.RatioDenominator != 0 && Framing->OutputCompression.RatioDenominator < Framing->OutputCompression.RatioNumerator) { /* framing request is ok */ return STATUS_SUCCESS; } return STATUS_INVALID_DEVICE_REQUEST; }