Sync with trunk rev.61910 to get latest improvements and bugfixes.
[reactos.git] / drivers / filesystems / npfs / datasup.c
1 /*
2 * PROJECT: ReactOS Named Pipe FileSystem
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/filesystems/npfs/datasup.c
5 * PURPOSE: Data Queues Support
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "npfs.h"
12
13 // File ID number for NPFS bugchecking support
14 #define NPFS_BUGCHECK_FILE_ID (NPFS_BUGCHECK_DATASUP)
15
16 /* FUNCTIONS ******************************************************************/
17
18 NTSTATUS
19 NTAPI
20 NpUninitializeDataQueue(IN PNP_DATA_QUEUE DataQueue)
21 {
22 PAGED_CODE();
23
24 ASSERT(DataQueue->QueueState == Empty);
25
26 RtlZeroMemory(DataQueue, sizeof(*DataQueue));
27 return STATUS_SUCCESS;
28 }
29
30 NTSTATUS
31 NTAPI
32 NpInitializeDataQueue(IN PNP_DATA_QUEUE DataQueue,
33 IN ULONG Quota)
34 {
35 PAGED_CODE();
36
37 DataQueue->BytesInQueue = 0;
38 DataQueue->EntriesInQueue = 0;
39 DataQueue->QuotaUsed = 0;
40 DataQueue->ByteOffset = 0;
41 DataQueue->QueueState = Empty;
42 DataQueue->Quota = Quota;
43 InitializeListHead(&DataQueue->Queue);
44 return STATUS_SUCCESS;
45 }
46
47 VOID
48 NTAPI
49 NpCompleteStalledWrites(IN PNP_DATA_QUEUE DataQueue,
50 IN PLIST_ENTRY List)
51 {
52 ULONG QuotaLeft, ByteOffset, DataLeft, NewQuotaLeft;
53 PNP_DATA_QUEUE_ENTRY DataQueueEntry;
54 PIRP Irp;
55 PLIST_ENTRY NextEntry;
56
57 QuotaLeft = DataQueue->Quota - DataQueue->QuotaUsed;
58 ByteOffset = DataQueue->ByteOffset;
59
60 NextEntry = DataQueue->Queue.Flink;
61 while (NextEntry != &DataQueue->Queue)
62 {
63 if (!QuotaLeft) break;
64
65 DataQueueEntry = CONTAINING_RECORD(NextEntry, NP_DATA_QUEUE_ENTRY, Irp);
66
67 Irp = DataQueueEntry->Irp;
68
69 if ((DataQueueEntry->DataEntryType == 0) && (Irp))
70 {
71 DataLeft = DataQueueEntry->DataSize - ByteOffset;
72
73 if (DataQueueEntry->QuotaInEntry < DataLeft)
74 {
75 NewQuotaLeft = DataLeft - DataQueueEntry->QuotaInEntry;
76 if (NewQuotaLeft > QuotaLeft) NewQuotaLeft = QuotaLeft;
77
78 QuotaLeft -= NewQuotaLeft;
79 DataQueueEntry->QuotaInEntry += NewQuotaLeft;
80
81 if (DataQueueEntry->QuotaInEntry == DataLeft &&
82 IoSetCancelRoutine(Irp, NULL))
83 {
84 DataQueueEntry->Irp = NULL;
85
86 Irp->IoStatus.Status = 0;
87 Irp->IoStatus.Information = DataQueueEntry->DataSize;
88
89 InsertTailList(List, &Irp->Tail.Overlay.ListEntry);
90 }
91 }
92 }
93
94 NextEntry = NextEntry->Flink;
95 ByteOffset = 0;
96 }
97
98 DataQueue->QuotaUsed = DataQueue->Quota - QuotaLeft;
99 }
100
101 PIRP
102 NTAPI
103 NpRemoveDataQueueEntry(IN PNP_DATA_QUEUE DataQueue,
104 IN BOOLEAN Flag,
105 IN PLIST_ENTRY List)
106 {
107 PIRP Irp;
108 PNP_DATA_QUEUE_ENTRY QueueEntry;
109 BOOLEAN HasWrites;
110
111 if (DataQueue->QueueState == Empty)
112 {
113 Irp = NULL;
114 ASSERT(IsListEmpty(&DataQueue->Queue));
115 ASSERT(DataQueue->EntriesInQueue == 0);
116 ASSERT(DataQueue->BytesInQueue == 0);
117 ASSERT(DataQueue->QuotaUsed == 0);
118 }
119 else
120 {
121 QueueEntry = CONTAINING_RECORD(RemoveHeadList(&DataQueue->Queue),
122 NP_DATA_QUEUE_ENTRY,
123 QueueEntry);
124
125 DataQueue->BytesInQueue -= QueueEntry->DataSize;
126 --DataQueue->EntriesInQueue;
127
128 HasWrites = 1;
129 if (DataQueue->QueueState != WriteEntries ||
130 DataQueue->QuotaUsed < DataQueue->Quota ||
131 !QueueEntry->QuotaInEntry)
132 {
133 HasWrites = 0;
134 }
135
136 DataQueue->QuotaUsed -= QueueEntry->QuotaInEntry;
137
138 if (IsListEmpty(&DataQueue->Queue))
139 {
140 DataQueue->QueueState = Empty;
141 HasWrites = 0;
142 }
143
144 Irp = QueueEntry->Irp;
145 NpFreeClientSecurityContext(QueueEntry->ClientSecurityContext);
146
147 if (Irp && IoSetCancelRoutine(Irp, NULL))
148 {
149 Irp->Tail.Overlay.DriverContext[3] = NULL;
150 }
151
152 ExFreePool(QueueEntry);
153
154 if (Flag)
155 {
156 NpGetNextRealDataQueueEntry(DataQueue, List);
157 }
158
159 if (HasWrites)
160 {
161 NpCompleteStalledWrites(DataQueue, List);
162 }
163 }
164
165 DataQueue->ByteOffset = 0;
166 return Irp;
167 }
168
169 PLIST_ENTRY
170 NTAPI
171 NpGetNextRealDataQueueEntry(IN PNP_DATA_QUEUE DataQueue,
172 IN PLIST_ENTRY List)
173 {
174 PNP_DATA_QUEUE_ENTRY DataEntry;
175 ULONG Type;
176 PIRP Irp;
177 PLIST_ENTRY NextEntry;
178 PAGED_CODE();
179
180 for (NextEntry = DataQueue->Queue.Flink;
181 NextEntry != &DataQueue->Queue;
182 NextEntry = DataQueue->Queue.Flink)
183 {
184 DataEntry = CONTAINING_RECORD(NextEntry, NP_DATA_QUEUE_ENTRY, QueueEntry);
185
186 Type = DataEntry->DataEntryType;
187 if (Type == Buffered || Type == Unbuffered) break;
188
189 Irp = NpRemoveDataQueueEntry(DataQueue, FALSE, List);
190 if (Irp)
191 {
192 Irp->IoStatus.Status = STATUS_SUCCESS;
193 InsertTailList(List, &Irp->Tail.Overlay.ListEntry);
194 }
195 }
196
197 return NextEntry;
198 }
199
200 VOID
201 NTAPI
202 NpCancelDataQueueIrp(IN PDEVICE_OBJECT DeviceObject,
203 IN PIRP Irp)
204 {
205 PNP_DATA_QUEUE DataQueue;
206 PNP_DATA_QUEUE_ENTRY DataEntry;
207 LIST_ENTRY DeferredList;
208 PSECURITY_CLIENT_CONTEXT ClientSecurityContext;
209 BOOLEAN CompleteWrites, FirstEntry;
210
211 if (DeviceObject) IoReleaseCancelSpinLock(Irp->CancelIrql);
212
213 InitializeListHead(&DeferredList);
214
215 DataQueue = (PNP_DATA_QUEUE)Irp->Tail.Overlay.DriverContext[2];
216 ClientSecurityContext = NULL;
217
218 if (DeviceObject)
219 {
220 FsRtlEnterFileSystem();
221 NpAcquireExclusiveVcb();
222 }
223
224 DataEntry = (PNP_DATA_QUEUE_ENTRY)Irp->Tail.Overlay.DriverContext[3];
225 if (DataEntry)
226 {
227 if (DataEntry->QueueEntry.Blink == &DataQueue->Queue)
228 {
229 DataQueue->ByteOffset = 0;
230 FirstEntry = 1;
231 }
232 else
233 {
234 FirstEntry = 0;
235 }
236
237 RemoveEntryList(&DataEntry->QueueEntry);
238
239 ClientSecurityContext = DataEntry->ClientSecurityContext;
240
241 CompleteWrites = 1;
242 if (DataQueue->QueueState != WriteEntries ||
243 DataQueue->QuotaUsed < DataQueue->Quota ||
244 !DataEntry->QuotaInEntry)
245 {
246 CompleteWrites = 0;
247 }
248
249 DataQueue->BytesInQueue -= DataEntry->DataSize;
250 DataQueue->QuotaUsed -= DataEntry->QuotaInEntry;
251 --DataQueue->EntriesInQueue;
252
253 if (IsListEmpty(&DataQueue->Queue))
254 {
255 DataQueue->QueueState = Empty;
256 ASSERT(DataQueue->BytesInQueue == 0);
257 ASSERT(DataQueue->EntriesInQueue == 0);
258 ASSERT(DataQueue->QuotaUsed == 0);
259 }
260 else
261 {
262 if (FirstEntry)
263 {
264 NpGetNextRealDataQueueEntry(DataQueue, &DeferredList);
265 }
266 if (CompleteWrites)
267 {
268 NpCompleteStalledWrites(DataQueue, &DeferredList);
269 }
270 }
271 }
272
273 if (DeviceObject)
274 {
275 NpReleaseVcb();
276 FsRtlExitFileSystem();
277 }
278
279 if (DataEntry) ExFreePool(DataEntry);
280
281 NpFreeClientSecurityContext(ClientSecurityContext);
282 Irp->IoStatus.Status = STATUS_CANCELLED;
283 IoCompleteRequest(Irp, IO_NAMED_PIPE_INCREMENT);
284
285 NpCompleteDeferredIrps(&DeferredList);
286 }
287
288 NTSTATUS
289 NTAPI
290 NpAddDataQueueEntry(IN ULONG NamedPipeEnd,
291 IN PNP_CCB Ccb,
292 IN PNP_DATA_QUEUE DataQueue,
293 IN ULONG Who,
294 IN ULONG Type,
295 IN ULONG DataSize,
296 IN PIRP Irp,
297 IN PVOID Buffer,
298 IN ULONG ByteOffset)
299 {
300 NTSTATUS Status;
301 PNP_DATA_QUEUE_ENTRY DataEntry;
302 SIZE_T EntrySize;
303 ULONG QuotaInEntry;
304 PSECURITY_CLIENT_CONTEXT ClientContext;
305 BOOLEAN HasSpace;
306
307 ClientContext = NULL;
308 ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));
309
310 Status = STATUS_SUCCESS;
311
312 if ((Type != 2) && (Who == WriteEntries))
313 {
314 Status = NpGetClientSecurityContext(NamedPipeEnd,
315 Ccb,
316 Irp ? Irp->Tail.Overlay.Thread :
317 PsGetCurrentThread(),
318 &ClientContext);
319 if (!NT_SUCCESS(Status))
320 {
321 return Status;
322 }
323 }
324
325 switch (Type)
326 {
327 case Unbuffered:
328 case 2:
329 case 3:
330
331 ASSERT(Irp != NULL);
332 DataEntry = ExAllocatePoolWithQuotaTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
333 sizeof(*DataEntry),
334 NPFS_DATA_ENTRY_TAG);
335 if (!DataEntry)
336 {
337 NpFreeClientSecurityContext(ClientContext);
338 return STATUS_INSUFFICIENT_RESOURCES;
339 }
340
341 DataEntry->DataEntryType = Type;
342 DataEntry->QuotaInEntry = 0;
343 DataEntry->Irp = Irp;
344 DataEntry->DataSize = DataSize;
345 DataEntry->ClientSecurityContext = ClientContext;
346 ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));
347 Status = STATUS_PENDING;
348 break;
349
350 case Buffered:
351
352 EntrySize = sizeof(*DataEntry);
353 if (Who != ReadEntries)
354 {
355 EntrySize += DataSize;
356 if (EntrySize < DataSize)
357 {
358 NpFreeClientSecurityContext(ClientContext);
359 return STATUS_INVALID_PARAMETER;
360 }
361 }
362
363 QuotaInEntry = DataSize - ByteOffset;
364 if (DataQueue->Quota - DataQueue->QuotaUsed < QuotaInEntry)
365 {
366 QuotaInEntry = DataQueue->Quota - DataQueue->QuotaUsed;
367 HasSpace = TRUE;
368 }
369 else
370 {
371 HasSpace = FALSE;
372 }
373
374 DataEntry = ExAllocatePoolWithQuotaTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
375 EntrySize,
376 NPFS_DATA_ENTRY_TAG);
377 if (!DataEntry)
378 {
379 NpFreeClientSecurityContext(ClientContext);
380 return STATUS_INSUFFICIENT_RESOURCES;
381 }
382
383 DataEntry->QuotaInEntry = QuotaInEntry;
384 DataEntry->Irp = Irp;
385 DataEntry->DataEntryType = Buffered;
386 DataEntry->ClientSecurityContext = ClientContext;
387 DataEntry->DataSize = DataSize;
388
389 if (Who == ReadEntries)
390 {
391 ASSERT(Irp);
392
393 Status = STATUS_PENDING;
394 ASSERT((DataQueue->QueueState == Empty) ||
395 (DataQueue->QueueState == Who));
396 }
397 else
398 {
399 _SEH2_TRY
400 {
401 RtlCopyMemory(DataEntry + 1,
402 Irp ? Irp->UserBuffer: Buffer,
403 DataSize);
404 }
405 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
406 {
407 NpFreeClientSecurityContext(ClientContext);
408 _SEH2_YIELD(return _SEH2_GetExceptionCode());
409 }
410 _SEH2_END;
411
412 if (HasSpace && Irp)
413 {
414 Status = STATUS_PENDING;
415 }
416 else
417 {
418 DataEntry->Irp = NULL;
419 Status = STATUS_SUCCESS;
420 }
421
422 ASSERT((DataQueue->QueueState == Empty) ||
423 (DataQueue->QueueState == Who));
424 }
425 break;
426
427 default:
428 ASSERT(FALSE);
429 NpFreeClientSecurityContext(ClientContext);
430 return STATUS_INVALID_PARAMETER;
431 }
432
433 ASSERT((DataQueue->QueueState == Empty) || (DataQueue->QueueState == Who));
434 if (DataQueue->QueueState == Empty)
435 {
436 ASSERT(DataQueue->BytesInQueue == 0);
437 ASSERT(DataQueue->EntriesInQueue == 0);
438 ASSERT(IsListEmpty(&DataQueue->Queue));
439 }
440 else
441 {
442 ASSERT(DataQueue->QueueState == Who);
443 ASSERT(DataQueue->QueueState != Empty);
444 ASSERT(DataQueue->EntriesInQueue != 0);
445 }
446
447 DataQueue->QuotaUsed += DataEntry->QuotaInEntry;
448 DataQueue->QueueState = Who;
449 DataQueue->BytesInQueue += DataEntry->DataSize;
450 DataQueue->EntriesInQueue++;
451
452 if (ByteOffset)
453 {
454 DataQueue->ByteOffset = ByteOffset;
455 ASSERT(Who == WriteEntries);
456 ASSERT(ByteOffset < DataEntry->DataSize);
457 ASSERT(DataQueue->EntriesInQueue == 1);
458 }
459
460 InsertTailList(&DataQueue->Queue, &DataEntry->QueueEntry);
461
462 if (Status == STATUS_PENDING)
463 {
464 IoMarkIrpPending(Irp);
465 Irp->Tail.Overlay.DriverContext[2] = DataQueue;
466 Irp->Tail.Overlay.DriverContext[3] = DataEntry;
467
468 IoSetCancelRoutine(Irp, NpCancelDataQueueIrp);
469
470 if ((Irp->Cancel) && (IoSetCancelRoutine(Irp, NULL)))
471 {
472 NpCancelDataQueueIrp(NULL, Irp);
473 }
474 }
475
476 return Status;
477 }
478
479 /* EOF */