Merge amd64 NDK from amd64 branch:
[reactos.git] / reactos / drivers / ksfilter / ks / bag.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/ksfilter/ks/bag.c
5 * PURPOSE: KS Object Bag functions
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9
10 #include "priv.h"
11
12 typedef struct
13 {
14 LIST_ENTRY Entry;
15 PVOID Item;
16 PFNKSFREE Free;
17 ULONG References;
18 }KSIOBJECT_BAG_ENTRY, *PKSIOBJECT_BAG_ENTRY;
19
20
21 /*
22 @implemented
23 */
24 KSDDKAPI
25 NTSTATUS
26 NTAPI
27 KsAllocateObjectBag(
28 IN PKSDEVICE Device,
29 OUT KSOBJECT_BAG* ObjectBag)
30 {
31 PKSIDEVICE_HEADER DeviceHeader;
32 PKSIOBJECT_BAG Bag;
33 IKsDevice *KsDevice;
34
35 /* get real device header */
36 DeviceHeader = (PKSIDEVICE_HEADER)CONTAINING_RECORD(Device, KSIDEVICE_HEADER, KsDevice);
37
38 /* allocate a object bag ctx */
39 Bag = AllocateItem(NonPagedPool, sizeof(KSIOBJECT_BAG));
40 if (!Bag)
41 return STATUS_INSUFFICIENT_RESOURCES;
42
43 /* get device interface */
44 KsDevice = (IKsDevice*)&DeviceHeader->lpVtblIKsDevice;
45
46 /* initialize object bag */
47 return KsDevice->lpVtbl->InitializeObjectBag(KsDevice, Bag, NULL);
48 }
49
50 PKSIOBJECT_BAG_ENTRY
51 KspFindObjectBagItem(
52 IN PLIST_ENTRY ObjectList,
53 IN PVOID Item)
54 {
55 PLIST_ENTRY Entry;
56 PKSIOBJECT_BAG_ENTRY BagEntry;
57
58 /* point to first item */
59 Entry = ObjectList->Flink;
60 /* first scan the list if the item is already inserted */
61 while(Entry != ObjectList)
62 {
63 /* get bag entry */
64 BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG_ENTRY, Entry);
65
66 if (BagEntry->Item == Item)
67 {
68 /* found entry */
69 return BagEntry;
70 }
71 /* move to next entry */
72 Entry = Entry->Flink;
73 }
74 /* item not in this object bag */
75 return NULL;
76 }
77
78
79 /*
80 @implemented
81 */
82 NTSTATUS
83 NTAPI
84 KsAddItemToObjectBag(
85 IN KSOBJECT_BAG ObjectBag,
86 IN PVOID Item,
87 IN PFNKSFREE Free OPTIONAL)
88 {
89 PKSIOBJECT_BAG Bag;
90 PKSIOBJECT_BAG_ENTRY BagEntry;
91
92 /* get real object bag */
93 Bag = (PKSIOBJECT_BAG)ObjectBag;
94
95 /* acquire bag mutex */
96 KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
97
98 /* is the item already present in this object bag */
99 BagEntry = KspFindObjectBagItem(&Bag->ObjectList, Item);
100
101 if (BagEntry)
102 {
103 /* is is, update reference count */
104 InterlockedIncrement((PLONG)&BagEntry->References);
105 /* release mutex */
106 KeReleaseMutex(Bag->BagMutex, FALSE);
107 /* return result */
108 return STATUS_SUCCESS;
109 }
110
111 /* item is new, allocate entry */
112 BagEntry = AllocateItem(NonPagedPool, sizeof(KSIOBJECT_BAG_ENTRY));
113 if (!BagEntry)
114 {
115 /* no memory */
116 KeReleaseMutex(Bag->BagMutex, FALSE);
117 /* return result */
118 return STATUS_INSUFFICIENT_RESOURCES;
119 }
120
121 /* initialize bag entry */
122 BagEntry->References = 1;
123 BagEntry->Item = Item;
124 if (Free)
125 BagEntry->Free = Free;
126 else
127 BagEntry->Free = ExFreePool;
128
129 /* insert item */
130 InsertTailList(&Bag->ObjectList, &Bag->Entry);
131
132 /* release mutex */
133 KeReleaseMutex(Bag->BagMutex, FALSE);
134
135 /* done */
136 return STATUS_SUCCESS;
137 }
138
139 ULONG
140 KspGetObjectItemReferenceCount(
141 IN PKSIDEVICE_HEADER DeviceHeader,
142 IN PVOID Item)
143 {
144 PLIST_ENTRY Entry;
145 PKSIOBJECT_BAG OtherBag;
146 PKSIOBJECT_BAG_ENTRY OtherBagEntry;
147 ULONG TotalRefs = 0;
148
149 /* scan all object bags and see if item is present there */
150 Entry = DeviceHeader->ObjectBags.Flink;
151 while(Entry != &DeviceHeader->ObjectBags)
152 {
153 /* get other bag */
154 OtherBag = (PKSIOBJECT_BAG)CONTAINING_RECORD(Entry, KSIOBJECT_BAG, Entry);
155
156 /* is the item present there */
157 OtherBagEntry = KspFindObjectBagItem(&OtherBag->ObjectList, Item);
158
159 if (OtherBagEntry)
160 TotalRefs++;
161
162 /* move to next item */
163 Entry = Entry->Flink;
164 }
165
166 return TotalRefs;
167 }
168
169 /*
170 @implemented
171 */
172 KSDDKAPI
173 ULONG
174 NTAPI
175 KsRemoveItemFromObjectBag(
176 IN KSOBJECT_BAG ObjectBag,
177 IN PVOID Item,
178 IN BOOLEAN Free)
179 {
180 PKSIOBJECT_BAG Bag;
181 PKSIOBJECT_BAG_ENTRY BagEntry;
182 ULONG TotalRefs;
183
184 /* get real object bag */
185 Bag = (PKSIOBJECT_BAG)ObjectBag;
186
187 /* acquire bag mutex */
188 KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
189
190 /* is the item already present in this object bag */
191 BagEntry = KspFindObjectBagItem(&Bag->ObjectList, Item);
192
193 if (!BagEntry)
194 {
195 /* item was not in this object bag */
196 KeReleaseMutex(Bag->BagMutex, FALSE);
197 return 0;
198 }
199
200 /* set current refs count */
201 TotalRefs = BagEntry->References;
202
203 /* get total refs count */
204 TotalRefs += KspGetObjectItemReferenceCount((PKSIDEVICE_HEADER)Bag->DeviceHeader, Item);
205
206 /* decrease reference count */
207 InterlockedDecrement((PLONG)&BagEntry->References);
208
209 if (BagEntry->References == 0)
210 {
211 /* remove the entry */
212 RemoveEntryList(&BagEntry->Entry);
213 }
214
215 if (TotalRefs == 1)
216 {
217 /* does the caller want to free the item */
218 if (Free)
219 {
220 /* free the item */
221 BagEntry->Free(BagEntry->Item);
222 }
223 }
224 if (BagEntry->References == 0)
225 {
226 /* free bag item entry */
227 FreeItem(BagEntry);
228 }
229
230 /* release mutex */
231 KeReleaseMutex(Bag->BagMutex, FALSE);
232
233
234 return TotalRefs;
235 }
236
237 /*
238 @implemented
239 */
240 KSDDKAPI
241 NTSTATUS
242 NTAPI
243 KsCopyObjectBagItems(
244 IN KSOBJECT_BAG ObjectBagDestination,
245 IN KSOBJECT_BAG ObjectBagSource)
246 {
247 PKSIOBJECT_BAG ObjectBagDest, ObjectBagSrc;
248 PLIST_ENTRY Entry;
249 PKSIOBJECT_BAG_ENTRY BagEntry;
250 NTSTATUS Status = STATUS_SUCCESS;
251
252 /* get object bag src */
253 ObjectBagSrc = (PKSIOBJECT_BAG)ObjectBagSource;
254
255 /* get object bag dst */
256 ObjectBagDest = (PKSIOBJECT_BAG)ObjectBagDestination;
257
258 /* acquire source mutex */
259 KeWaitForSingleObject(ObjectBagSrc->BagMutex, Executive, KernelMode, FALSE, NULL);
260
261 if (ObjectBagSrc->BagMutex != ObjectBagDest->BagMutex)
262 {
263 /* acquire destination mutex */
264 KeWaitForSingleObject(ObjectBagDest->BagMutex, Executive, KernelMode, FALSE, NULL);
265 }
266
267 /* point to first item */
268 Entry = ObjectBagSrc->ObjectList.Flink;
269 /* first scan the list if the item is already inserted */
270 while(Entry != &ObjectBagSrc->ObjectList)
271 {
272 /* get bag entry */
273 BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG_ENTRY, Entry);
274
275 /* add the item */
276 Status = KsAddItemToObjectBag(ObjectBagDestination, BagEntry->Item, BagEntry->Free);
277
278 /* check for success */
279 if (!NT_SUCCESS(Status))
280 break;
281
282 /* move to next entry */
283 Entry = Entry->Flink;
284 }
285
286 if (ObjectBagSrc->BagMutex != ObjectBagDest->BagMutex)
287 {
288 /* release destination mutex */
289 KeReleaseMutex(ObjectBagDest->BagMutex, FALSE);
290 }
291
292 /* release source mutex */
293 KeReleaseMutex(ObjectBagSrc->BagMutex, FALSE);
294
295 return Status;
296 }
297
298 /*
299 @implemented
300 */
301 KSDDKAPI
302 VOID
303 NTAPI
304 KsFreeObjectBag(
305 IN KSOBJECT_BAG ObjectBag)
306 {
307 PLIST_ENTRY Entry;
308 PKSIOBJECT_BAG Bag;
309 PKSIOBJECT_BAG_ENTRY BagEntry;
310 ULONG TotalRefs;
311
312 /* get real object bag */
313 Bag = (PKSIOBJECT_BAG)ObjectBag;
314
315 /* acquire bag mutex */
316 KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
317
318 while(!IsListEmpty(&Bag->ObjectList))
319 {
320 /* get an bag entry */
321 Entry = RemoveHeadList(&Bag->ObjectList);
322 /* access bag entry item */
323 BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG, Entry);
324
325 /* check if the item is present in some other bag */
326 TotalRefs = KspGetObjectItemReferenceCount((PKSIDEVICE_HEADER)Bag->DeviceHeader, &BagEntry->Item);
327
328 if (TotalRefs == 0)
329 {
330 /* item is ready to be freed */
331 BagEntry->Free(BagEntry->Item);
332 }
333
334 /* free bag entry item */
335 FreeItem(BagEntry);
336 }
337
338 /* remove bag entry from device object list */
339 RemoveEntryList(&Bag->Entry);
340
341 /* release bag mutex */
342 KeReleaseMutex(Bag->BagMutex, FALSE);
343
344 /* now free object bag */
345 FreeItem(Bag);
346 }
347
348 /*
349 @implemented
350 */
351 KSDDKAPI
352 NTSTATUS
353 NTAPI
354 _KsEdit(
355 IN KSOBJECT_BAG ObjectBag,
356 IN OUT PVOID* PointerToPointerToItem,
357 IN ULONG NewSize,
358 IN ULONG OldSize,
359 IN ULONG Tag)
360 {
361 PKSIOBJECT_BAG Bag;
362 PKSIOBJECT_BAG_ENTRY BagEntry;
363 PVOID Item;
364 NTSTATUS Status;
365
366 /* get real object bag */
367 Bag = (PKSIOBJECT_BAG)ObjectBag;
368
369 /* acquire bag mutex */
370 KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
371
372
373 if (*PointerToPointerToItem)
374 {
375 /* search object bag for this entry */
376 BagEntry = KspFindObjectBagItem(&Bag->ObjectList, *PointerToPointerToItem);
377 }
378 else
379 {
380 /* pointer to null, allocate new entry */
381 BagEntry = NULL;
382 }
383
384 if (!BagEntry || NewSize > OldSize)
385 {
386 /* entry does not exist or new entry must be allocated */
387 Item = AllocateItem(NonPagedPool, NewSize);
388
389 if (!Item)
390 {
391 /* not enough resources */
392 KeReleaseMutex(Bag->BagMutex, FALSE);
393 return STATUS_INSUFFICIENT_RESOURCES;
394 }
395
396 /* now add the item to the object bag */
397 Status = KsAddItemToObjectBag((KSOBJECT_BAG)Bag, Item, NULL);
398 /* check for success */
399 if (!NT_SUCCESS(Status))
400 {
401 /* failed to add item */
402 FreeItem(Item);
403 KeReleaseMutex(Bag->BagMutex, FALSE);
404 return Status;
405 }
406
407 if (*PointerToPointerToItem)
408 {
409 /* object exists */
410 if (OldSize >= NewSize)
411 {
412 /* copy old contents */
413 RtlMoveMemory(Item, *PointerToPointerToItem, NewSize);
414 }
415 else
416 {
417 /* copy new contents */
418 RtlMoveMemory(Item, *PointerToPointerToItem, OldSize);
419 }
420 }
421
422 if (BagEntry)
423 {
424 /* remove old entry */
425 KsRemoveItemFromObjectBag(ObjectBag, BagEntry->Item, TRUE);
426 }
427
428 /* store item */
429 *PointerToPointerToItem = Item;
430 }
431
432 /* release bag mutex */
433 KeReleaseMutex(Bag->BagMutex, FALSE);
434
435 return STATUS_SUCCESS;
436 }
437
438