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