Sync to trunk (r47832)
[reactos.git] / 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->BasicHeader.OuterUnknown;
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 DPRINT("KsAddItemToObjectBag\n");
93
94 /* get real object bag */
95 Bag = (PKSIOBJECT_BAG)ObjectBag;
96
97 /* acquire bag mutex */
98 KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
99
100 /* is the item already present in this object bag */
101 BagEntry = KspFindObjectBagItem(&Bag->ObjectList, Item);
102
103 if (BagEntry)
104 {
105 /* is is, update reference count */
106 InterlockedIncrement((PLONG)&BagEntry->References);
107 /* release mutex */
108 KeReleaseMutex(Bag->BagMutex, FALSE);
109 /* return result */
110 return STATUS_SUCCESS;
111 }
112
113 /* item is new, allocate entry */
114 BagEntry = AllocateItem(NonPagedPool, sizeof(KSIOBJECT_BAG_ENTRY));
115 if (!BagEntry)
116 {
117 /* no memory */
118 KeReleaseMutex(Bag->BagMutex, FALSE);
119 /* return result */
120 return STATUS_INSUFFICIENT_RESOURCES;
121 }
122
123 /* initialize bag entry */
124 BagEntry->References = 1;
125 BagEntry->Item = Item;
126 if (Free)
127 BagEntry->Free = Free;
128 else
129 BagEntry->Free = ExFreePool;
130
131 /* insert item */
132 InsertTailList(&Bag->ObjectList, &BagEntry->Entry);
133
134 /* release mutex */
135 KeReleaseMutex(Bag->BagMutex, FALSE);
136
137 /* done */
138 return STATUS_SUCCESS;
139 }
140
141 ULONG
142 KspGetObjectItemReferenceCount(
143 IN PKSIDEVICE_HEADER DeviceHeader,
144 IN PVOID Item)
145 {
146 PLIST_ENTRY Entry;
147 PKSIOBJECT_BAG OtherBag;
148 PKSIOBJECT_BAG_ENTRY OtherBagEntry;
149 ULONG TotalRefs = 0;
150
151 /* scan all object bags and see if item is present there */
152 Entry = DeviceHeader->ObjectBags.Flink;
153 while(Entry != &DeviceHeader->ObjectBags)
154 {
155 /* get other bag */
156 OtherBag = (PKSIOBJECT_BAG)CONTAINING_RECORD(Entry, KSIOBJECT_BAG, Entry);
157
158 /* is the item present there */
159 OtherBagEntry = KspFindObjectBagItem(&OtherBag->ObjectList, Item);
160
161 if (OtherBagEntry)
162 TotalRefs++;
163
164 /* move to next item */
165 Entry = Entry->Flink;
166 }
167
168 return TotalRefs;
169 }
170
171 /*
172 @implemented
173 */
174 KSDDKAPI
175 ULONG
176 NTAPI
177 KsRemoveItemFromObjectBag(
178 IN KSOBJECT_BAG ObjectBag,
179 IN PVOID Item,
180 IN BOOLEAN Free)
181 {
182 PKSIOBJECT_BAG Bag;
183 PKSIOBJECT_BAG_ENTRY BagEntry;
184 ULONG TotalRefs;
185
186 /* get real object bag */
187 Bag = (PKSIOBJECT_BAG)ObjectBag;
188
189 /* acquire bag mutex */
190 KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
191
192 /* is the item already present in this object bag */
193 BagEntry = KspFindObjectBagItem(&Bag->ObjectList, Item);
194
195 if (!BagEntry)
196 {
197 /* item was not in this object bag */
198 KeReleaseMutex(Bag->BagMutex, FALSE);
199 return 0;
200 }
201
202 /* set current refs count */
203 TotalRefs = BagEntry->References;
204
205 /* get total refs count */
206 TotalRefs += KspGetObjectItemReferenceCount((PKSIDEVICE_HEADER)Bag->DeviceHeader, Item);
207
208 /* decrease reference count */
209 InterlockedDecrement((PLONG)&BagEntry->References);
210
211 if (BagEntry->References == 0)
212 {
213 /* remove the entry */
214 RemoveEntryList(&BagEntry->Entry);
215 }
216
217 if (TotalRefs == 1)
218 {
219 /* does the caller want to free the item */
220 if (Free)
221 {
222 /* free the item */
223 BagEntry->Free(BagEntry->Item);
224 }
225 }
226 if (BagEntry->References == 0)
227 {
228 /* free bag item entry */
229 FreeItem(BagEntry);
230 }
231
232 /* release mutex */
233 KeReleaseMutex(Bag->BagMutex, FALSE);
234
235
236 return TotalRefs;
237 }
238
239 /*
240 @implemented
241 */
242 KSDDKAPI
243 NTSTATUS
244 NTAPI
245 KsCopyObjectBagItems(
246 IN KSOBJECT_BAG ObjectBagDestination,
247 IN KSOBJECT_BAG ObjectBagSource)
248 {
249 PKSIOBJECT_BAG ObjectBagDest, ObjectBagSrc;
250 PLIST_ENTRY Entry;
251 PKSIOBJECT_BAG_ENTRY BagEntry;
252 NTSTATUS Status = STATUS_SUCCESS;
253
254 /* get object bag src */
255 ObjectBagSrc = (PKSIOBJECT_BAG)ObjectBagSource;
256
257 /* get object bag dst */
258 ObjectBagDest = (PKSIOBJECT_BAG)ObjectBagDestination;
259
260 /* acquire source mutex */
261 KeWaitForSingleObject(ObjectBagSrc->BagMutex, Executive, KernelMode, FALSE, NULL);
262
263 if (ObjectBagSrc->BagMutex != ObjectBagDest->BagMutex)
264 {
265 /* acquire destination mutex */
266 KeWaitForSingleObject(ObjectBagDest->BagMutex, Executive, KernelMode, FALSE, NULL);
267 }
268
269 /* point to first item */
270 Entry = ObjectBagSrc->ObjectList.Flink;
271 /* first scan the list if the item is already inserted */
272 while(Entry != &ObjectBagSrc->ObjectList)
273 {
274 /* get bag entry */
275 BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG_ENTRY, Entry);
276
277 /* add the item */
278 Status = KsAddItemToObjectBag(ObjectBagDestination, BagEntry->Item, BagEntry->Free);
279
280 /* check for success */
281 if (!NT_SUCCESS(Status))
282 break;
283
284 /* move to next entry */
285 Entry = Entry->Flink;
286 }
287
288 if (ObjectBagSrc->BagMutex != ObjectBagDest->BagMutex)
289 {
290 /* release destination mutex */
291 KeReleaseMutex(ObjectBagDest->BagMutex, FALSE);
292 }
293
294 /* release source mutex */
295 KeReleaseMutex(ObjectBagSrc->BagMutex, FALSE);
296
297 return Status;
298 }
299
300 /*
301 @implemented
302 */
303 KSDDKAPI
304 VOID
305 NTAPI
306 KsFreeObjectBag(
307 IN KSOBJECT_BAG ObjectBag)
308 {
309 PLIST_ENTRY Entry;
310 PKSIOBJECT_BAG Bag;
311 PKSIOBJECT_BAG_ENTRY BagEntry;
312 ULONG TotalRefs;
313
314 /* get real object bag */
315 Bag = (PKSIOBJECT_BAG)ObjectBag;
316
317 /* acquire bag mutex */
318 KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
319
320 while(!IsListEmpty(&Bag->ObjectList))
321 {
322 /* get an bag entry */
323 Entry = RemoveHeadList(&Bag->ObjectList);
324 /* access bag entry item */
325 BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG, Entry);
326
327 /* check if the item is present in some other bag */
328 TotalRefs = KspGetObjectItemReferenceCount((PKSIDEVICE_HEADER)Bag->DeviceHeader, &BagEntry->Item);
329
330 if (TotalRefs == 0)
331 {
332 /* item is ready to be freed */
333 BagEntry->Free(BagEntry->Item);
334 }
335
336 /* free bag entry item */
337 FreeItem(BagEntry);
338 }
339
340 /* remove bag entry from device object list */
341 RemoveEntryList(&Bag->Entry);
342
343 /* release bag mutex */
344 KeReleaseMutex(Bag->BagMutex, FALSE);
345
346 /* now free object bag */
347 FreeItem(Bag);
348 }
349
350 /*
351 @implemented
352 */
353 KSDDKAPI
354 NTSTATUS
355 NTAPI
356 _KsEdit(
357 IN KSOBJECT_BAG ObjectBag,
358 IN OUT PVOID* PointerToPointerToItem,
359 IN ULONG NewSize,
360 IN ULONG OldSize,
361 IN ULONG Tag)
362 {
363 PKSIOBJECT_BAG Bag;
364 PKSIOBJECT_BAG_ENTRY BagEntry;
365 PVOID Item;
366 NTSTATUS Status;
367
368 DPRINT("_KsEdit\n");
369
370 /* get real object bag */
371 Bag = (PKSIOBJECT_BAG)ObjectBag;
372
373 /* acquire bag mutex */
374 KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
375
376
377 if (*PointerToPointerToItem)
378 {
379 /* search object bag for this entry */
380 BagEntry = KspFindObjectBagItem(&Bag->ObjectList, *PointerToPointerToItem);
381 }
382 else
383 {
384 /* pointer to null, allocate new entry */
385 BagEntry = NULL;
386 }
387
388 if (!BagEntry || NewSize > OldSize)
389 {
390 /* entry does not exist or new entry must be allocated */
391 Item = AllocateItem(NonPagedPool, NewSize);
392
393 if (!Item)
394 {
395 /* not enough resources */
396 KeReleaseMutex(Bag->BagMutex, FALSE);
397 return STATUS_INSUFFICIENT_RESOURCES;
398 }
399
400 /* now add the item to the object bag */
401 Status = KsAddItemToObjectBag((KSOBJECT_BAG)Bag, Item, NULL);
402 /* check for success */
403 if (!NT_SUCCESS(Status))
404 {
405 /* failed to add item */
406 FreeItem(Item);
407 KeReleaseMutex(Bag->BagMutex, FALSE);
408 return Status;
409 }
410
411 if (*PointerToPointerToItem)
412 {
413 /* object exists */
414 if (OldSize >= NewSize)
415 {
416 /* copy old contents */
417 RtlMoveMemory(Item, *PointerToPointerToItem, NewSize);
418 }
419 else
420 {
421 /* copy new contents */
422 RtlMoveMemory(Item, *PointerToPointerToItem, OldSize);
423 }
424 }
425
426 if (BagEntry)
427 {
428 /* remove old entry */
429 KsRemoveItemFromObjectBag(ObjectBag, BagEntry->Item, TRUE);
430 }
431
432 /* store item */
433 *PointerToPointerToItem = Item;
434 }
435
436 /* release bag mutex */
437 KeReleaseMutex(Bag->BagMutex, FALSE);
438
439 return STATUS_SUCCESS;
440 }
441
442