reactos/drivers/ksfilter/ks/bag.c

445 lines
10 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Kernel Streaming
* FILE: drivers/ksfilter/ks/bag.c
* PURPOSE: KS Object Bag functions
* PROGRAMMER: Johannes Anderwald
*/
#include "precomp.h"
#define NDEBUG
#include <debug.h>
typedef struct
{
LIST_ENTRY Entry;
PVOID Item;
PFNKSFREE Free;
ULONG References;
}KSIOBJECT_BAG_ENTRY, *PKSIOBJECT_BAG_ENTRY;
/*
@implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsAllocateObjectBag(
IN PKSDEVICE Device,
OUT KSOBJECT_BAG* ObjectBag)
{
PKSIDEVICE_HEADER DeviceHeader;
PKSIOBJECT_BAG Bag;
IKsDevice *KsDevice;
/* get real device header */
DeviceHeader = (PKSIDEVICE_HEADER)CONTAINING_RECORD(Device, KSIDEVICE_HEADER, KsDevice);
/* allocate a object bag ctx */
Bag = AllocateItem(NonPagedPool, sizeof(KSIOBJECT_BAG));
if (!Bag)
return STATUS_INSUFFICIENT_RESOURCES;
/* get device interface */
KsDevice = (IKsDevice*)&DeviceHeader->BasicHeader.OuterUnknown;
/* initialize object bag */
return KsDevice->lpVtbl->InitializeObjectBag(KsDevice, Bag, NULL);
}
PKSIOBJECT_BAG_ENTRY
KspFindObjectBagItem(
IN PLIST_ENTRY ObjectList,
IN PVOID Item)
{
PLIST_ENTRY Entry;
PKSIOBJECT_BAG_ENTRY BagEntry;
/* point to first item */
Entry = ObjectList->Flink;
/* first scan the list if the item is already inserted */
while(Entry != ObjectList)
{
/* get bag entry */
BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG_ENTRY, Entry);
if (BagEntry->Item == Item)
{
/* found entry */
return BagEntry;
}
/* move to next entry */
Entry = Entry->Flink;
}
/* item not in this object bag */
return NULL;
}
/*
@implemented
*/
NTSTATUS
NTAPI
KsAddItemToObjectBag(
IN KSOBJECT_BAG ObjectBag,
IN PVOID Item,
IN PFNKSFREE Free OPTIONAL)
{
PKSIOBJECT_BAG Bag;
PKSIOBJECT_BAG_ENTRY BagEntry;
DPRINT("KsAddItemToObjectBag\n");
/* get real object bag */
Bag = (PKSIOBJECT_BAG)ObjectBag;
/* acquire bag mutex */
KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
/* is the item already present in this object bag */
BagEntry = KspFindObjectBagItem(&Bag->ObjectList, Item);
if (BagEntry)
{
/* is is, update reference count */
InterlockedIncrement((PLONG)&BagEntry->References);
/* release mutex */
KeReleaseMutex(Bag->BagMutex, FALSE);
/* return result */
return STATUS_SUCCESS;
}
/* item is new, allocate entry */
BagEntry = AllocateItem(NonPagedPool, sizeof(KSIOBJECT_BAG_ENTRY));
if (!BagEntry)
{
/* no memory */
KeReleaseMutex(Bag->BagMutex, FALSE);
/* return result */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* initialize bag entry */
BagEntry->References = 1;
BagEntry->Item = Item;
if (Free)
BagEntry->Free = Free;
else
BagEntry->Free = ExFreePool;
/* insert item */
InsertTailList(&Bag->ObjectList, &BagEntry->Entry);
/* release mutex */
KeReleaseMutex(Bag->BagMutex, FALSE);
/* done */
return STATUS_SUCCESS;
}
ULONG
KspGetObjectItemReferenceCount(
IN PKSIDEVICE_HEADER DeviceHeader,
IN PVOID Item)
{
PLIST_ENTRY Entry;
PKSIOBJECT_BAG OtherBag;
PKSIOBJECT_BAG_ENTRY OtherBagEntry;
ULONG TotalRefs = 0;
/* scan all object bags and see if item is present there */
Entry = DeviceHeader->ObjectBags.Flink;
while(Entry != &DeviceHeader->ObjectBags)
{
/* get other bag */
OtherBag = (PKSIOBJECT_BAG)CONTAINING_RECORD(Entry, KSIOBJECT_BAG, Entry);
/* is the item present there */
OtherBagEntry = KspFindObjectBagItem(&OtherBag->ObjectList, Item);
if (OtherBagEntry)
TotalRefs++;
/* move to next item */
Entry = Entry->Flink;
}
return TotalRefs;
}
/*
@implemented
*/
KSDDKAPI
ULONG
NTAPI
KsRemoveItemFromObjectBag(
IN KSOBJECT_BAG ObjectBag,
IN PVOID Item,
IN BOOLEAN Free)
{
PKSIOBJECT_BAG Bag;
PKSIOBJECT_BAG_ENTRY BagEntry;
ULONG TotalRefs;
/* get real object bag */
Bag = (PKSIOBJECT_BAG)ObjectBag;
/* acquire bag mutex */
KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
/* is the item already present in this object bag */
BagEntry = KspFindObjectBagItem(&Bag->ObjectList, Item);
if (!BagEntry)
{
/* item was not in this object bag */
KeReleaseMutex(Bag->BagMutex, FALSE);
return 0;
}
/* set current refs count */
TotalRefs = BagEntry->References;
/* get total refs count */
TotalRefs += KspGetObjectItemReferenceCount((PKSIDEVICE_HEADER)Bag->DeviceHeader, Item);
/* decrease reference count */
InterlockedDecrement((PLONG)&BagEntry->References);
if (BagEntry->References == 0)
{
/* remove the entry */
RemoveEntryList(&BagEntry->Entry);
}
if (TotalRefs == 1)
{
/* does the caller want to free the item */
if (Free)
{
/* free the item */
BagEntry->Free(BagEntry->Item);
}
}
if (BagEntry->References == 0)
{
/* free bag item entry */
FreeItem(BagEntry);
}
/* release mutex */
KeReleaseMutex(Bag->BagMutex, FALSE);
return TotalRefs;
}
/*
@implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsCopyObjectBagItems(
IN KSOBJECT_BAG ObjectBagDestination,
IN KSOBJECT_BAG ObjectBagSource)
{
PKSIOBJECT_BAG ObjectBagDest, ObjectBagSrc;
PLIST_ENTRY Entry;
PKSIOBJECT_BAG_ENTRY BagEntry;
NTSTATUS Status = STATUS_SUCCESS;
/* get object bag src */
ObjectBagSrc = (PKSIOBJECT_BAG)ObjectBagSource;
/* get object bag dst */
ObjectBagDest = (PKSIOBJECT_BAG)ObjectBagDestination;
/* acquire source mutex */
KeWaitForSingleObject(ObjectBagSrc->BagMutex, Executive, KernelMode, FALSE, NULL);
if (ObjectBagSrc->BagMutex != ObjectBagDest->BagMutex)
{
/* acquire destination mutex */
KeWaitForSingleObject(ObjectBagDest->BagMutex, Executive, KernelMode, FALSE, NULL);
}
/* point to first item */
Entry = ObjectBagSrc->ObjectList.Flink;
/* first scan the list if the item is already inserted */
while(Entry != &ObjectBagSrc->ObjectList)
{
/* get bag entry */
BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG_ENTRY, Entry);
/* add the item */
Status = KsAddItemToObjectBag(ObjectBagDestination, BagEntry->Item, BagEntry->Free);
/* check for success */
if (!NT_SUCCESS(Status))
break;
/* move to next entry */
Entry = Entry->Flink;
}
if (ObjectBagSrc->BagMutex != ObjectBagDest->BagMutex)
{
/* release destination mutex */
KeReleaseMutex(ObjectBagDest->BagMutex, FALSE);
}
/* release source mutex */
KeReleaseMutex(ObjectBagSrc->BagMutex, FALSE);
return Status;
}
/*
@implemented
*/
KSDDKAPI
VOID
NTAPI
KsFreeObjectBag(
IN KSOBJECT_BAG ObjectBag)
{
PLIST_ENTRY Entry;
PKSIOBJECT_BAG Bag;
PKSIOBJECT_BAG_ENTRY BagEntry;
ULONG TotalRefs;
/* get real object bag */
Bag = (PKSIOBJECT_BAG)ObjectBag;
/* acquire bag mutex */
KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
while(!IsListEmpty(&Bag->ObjectList))
{
/* get an bag entry */
Entry = RemoveHeadList(&Bag->ObjectList);
/* access bag entry item */
BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG, Entry);
/* check if the item is present in some other bag */
TotalRefs = KspGetObjectItemReferenceCount((PKSIDEVICE_HEADER)Bag->DeviceHeader, &BagEntry->Item);
if (TotalRefs == 0)
{
/* item is ready to be freed */
BagEntry->Free(BagEntry->Item);
}
/* free bag entry item */
FreeItem(BagEntry);
}
/* remove bag entry from device object list */
RemoveEntryList(&Bag->Entry);
/* release bag mutex */
KeReleaseMutex(Bag->BagMutex, FALSE);
/* now free object bag */
FreeItem(Bag);
}
/*
@implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
_KsEdit(
IN KSOBJECT_BAG ObjectBag,
IN OUT PVOID* PointerToPointerToItem,
IN ULONG NewSize,
IN ULONG OldSize,
IN ULONG Tag)
{
PKSIOBJECT_BAG Bag;
PKSIOBJECT_BAG_ENTRY BagEntry;
PVOID Item;
NTSTATUS Status;
DPRINT("_KsEdit\n");
/* get real object bag */
Bag = (PKSIOBJECT_BAG)ObjectBag;
/* acquire bag mutex */
KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
if (*PointerToPointerToItem)
{
/* search object bag for this entry */
BagEntry = KspFindObjectBagItem(&Bag->ObjectList, *PointerToPointerToItem);
}
else
{
/* pointer to null, allocate new entry */
BagEntry = NULL;
}
if (!BagEntry || NewSize > OldSize)
{
/* entry does not exist or new entry must be allocated */
Item = AllocateItem(NonPagedPool, NewSize);
if (!Item)
{
/* not enough resources */
KeReleaseMutex(Bag->BagMutex, FALSE);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* now add the item to the object bag */
Status = KsAddItemToObjectBag((KSOBJECT_BAG)Bag, Item, NULL);
/* check for success */
if (!NT_SUCCESS(Status))
{
/* failed to add item */
FreeItem(Item);
KeReleaseMutex(Bag->BagMutex, FALSE);
return Status;
}
if (*PointerToPointerToItem)
{
/* object exists */
if (OldSize >= NewSize)
{
/* copy old contents */
RtlMoveMemory(Item, *PointerToPointerToItem, NewSize);
}
else
{
/* copy new contents */
RtlMoveMemory(Item, *PointerToPointerToItem, OldSize);
}
}
if (BagEntry)
{
/* remove old entry */
KsRemoveItemFromObjectBag(ObjectBag, BagEntry->Item, TRUE);
}
/* store item */
*PointerToPointerToItem = Item;
}
/* release bag mutex */
KeReleaseMutex(Bag->BagMutex, FALSE);
return STATUS_SUCCESS;
}