xref: /reactos/drivers/filesystems/npfs/datasup.c (revision c2c66aff)
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
NpUninitializeDataQueue(IN PNP_DATA_QUEUE DataQueue)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
NpInitializeDataQueue(IN PNP_DATA_QUEUE DataQueue,IN ULONG Quota)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
NpCompleteStalledWrites(IN PNP_DATA_QUEUE DataQueue,IN PLIST_ENTRY List)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
NpRemoveDataQueueEntry(IN PNP_DATA_QUEUE DataQueue,IN BOOLEAN Flag,IN PLIST_ENTRY List)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
NpGetNextRealDataQueueEntry(IN PNP_DATA_QUEUE DataQueue,IN PLIST_ENTRY List)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
NpCancelDataQueueIrp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)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
NpAddDataQueueEntry(IN ULONG NamedPipeEnd,IN PNP_CCB Ccb,IN PNP_DATA_QUEUE DataQueue,IN ULONG Who,IN ULONG Type,IN ULONG DataSize,IN PIRP Irp,IN PVOID Buffer,IN ULONG ByteOffset)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 */
485