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