xref: /reactos/drivers/filesystems/npfs/waitsup.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/waitsup.c
5  * PURPOSE:     Pipes Waiting 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_WAITSUP)
15 
16 /* FUNCTIONS ******************************************************************/
17 
18 VOID
19 NTAPI
NpCancelWaitQueueIrp(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)20 NpCancelWaitQueueIrp(IN PDEVICE_OBJECT DeviceObject,
21                      IN PIRP Irp)
22 {
23     KIRQL OldIrql;
24     PNP_WAIT_QUEUE_ENTRY WaitEntry;
25     PNP_WAIT_QUEUE WaitQueue;
26 
27     IoReleaseCancelSpinLock(Irp->CancelIrql);
28 
29     WaitQueue = Irp->Tail.Overlay.DriverContext[0];
30 
31     KeAcquireSpinLock(&WaitQueue->WaitLock, &OldIrql);
32 
33     WaitEntry = Irp->Tail.Overlay.DriverContext[1];
34     if (WaitEntry)
35     {
36         RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
37         if (!KeCancelTimer(&WaitEntry->Timer))
38         {
39             WaitEntry->Irp = NULL;
40             WaitEntry = NULL;
41         }
42     }
43 
44     KeReleaseSpinLock(&WaitQueue->WaitLock, OldIrql);
45 
46     if (WaitEntry)
47     {
48         ObDereferenceObject(WaitEntry->FileObject);
49         ExFreePool(WaitEntry);
50     }
51 
52     Irp->IoStatus.Information = 0;
53     Irp->IoStatus.Status = STATUS_CANCELLED;
54     IoCompleteRequest(Irp, IO_NAMED_PIPE_INCREMENT);
55 }
56 
57 VOID
58 NTAPI
NpTimerDispatch(IN PKDPC Dpc,IN PVOID Context,IN PVOID Argument1,IN PVOID Argument2)59 NpTimerDispatch(IN PKDPC Dpc,
60                 IN PVOID Context,
61                 IN PVOID Argument1,
62                 IN PVOID Argument2)
63 {
64     PIRP Irp;
65     KIRQL OldIrql;
66     PNP_WAIT_QUEUE_ENTRY WaitEntry = Context;
67 
68     KeAcquireSpinLock(&WaitEntry->WaitQueue->WaitLock, &OldIrql);
69 
70     Irp = WaitEntry->Irp;
71     if (Irp)
72     {
73         RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
74 
75         if (!IoSetCancelRoutine(Irp, NULL))
76         {
77             Irp->Tail.Overlay.DriverContext[1] = NULL;
78             Irp = NULL;
79         }
80     }
81 
82     KeReleaseSpinLock(&WaitEntry->WaitQueue->WaitLock, OldIrql);
83 
84     if (Irp)
85     {
86         Irp->IoStatus.Status = STATUS_IO_TIMEOUT;
87         IoCompleteRequest(Irp, IO_NAMED_PIPE_INCREMENT);
88     }
89 
90     ObDereferenceObject(WaitEntry->FileObject);
91     ExFreePool(WaitEntry);
92 }
93 
94 VOID
95 NTAPI
NpInitializeWaitQueue(IN PNP_WAIT_QUEUE WaitQueue)96 NpInitializeWaitQueue(IN PNP_WAIT_QUEUE WaitQueue)
97 {
98     InitializeListHead(&WaitQueue->WaitList);
99     KeInitializeSpinLock(&WaitQueue->WaitLock);
100 }
101 
102 static
103 BOOLEAN
NpEqualUnicodeString(IN PCUNICODE_STRING String1,IN PCUNICODE_STRING String2)104 NpEqualUnicodeString(IN PCUNICODE_STRING String1,
105                      IN PCUNICODE_STRING String2)
106 {
107     SIZE_T EqualLength;
108 
109     if (String1->Length != String2->Length)
110         return FALSE;
111 
112     EqualLength = RtlCompareMemory(String1->Buffer,
113                                    String2->Buffer,
114                                    String1->Length);
115     return EqualLength == String1->Length;
116 }
117 
118 NTSTATUS
119 NTAPI
NpCancelWaiter(IN PNP_WAIT_QUEUE WaitQueue,IN PUNICODE_STRING PipePath,IN NTSTATUS Status,IN PLIST_ENTRY List)120 NpCancelWaiter(IN PNP_WAIT_QUEUE WaitQueue,
121                IN PUNICODE_STRING PipePath,
122                IN NTSTATUS Status,
123                IN PLIST_ENTRY List)
124 {
125     UNICODE_STRING PipePathUpper;
126     KIRQL OldIrql;
127     PWCHAR Buffer;
128     PLIST_ENTRY NextEntry;
129     PNP_WAIT_QUEUE_ENTRY WaitEntry, Linkage;
130     PIRP WaitIrp;
131     PFILE_PIPE_WAIT_FOR_BUFFER WaitBuffer;
132     UNICODE_STRING WaitName, PipeName;
133 
134     Linkage = NULL;
135 
136     Buffer = ExAllocatePoolWithTag(NonPagedPool,
137                                    PipePath->Length,
138                                    NPFS_WAIT_BLOCK_TAG);
139     if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
140 
141     RtlInitEmptyUnicodeString(&PipePathUpper, Buffer, PipePath->Length);
142     RtlUpcaseUnicodeString(&PipePathUpper, PipePath, FALSE);
143 
144     KeAcquireSpinLock(&WaitQueue->WaitLock, &OldIrql);
145 
146     NextEntry = WaitQueue->WaitList.Flink;
147     while (NextEntry != &WaitQueue->WaitList)
148     {
149         WaitIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
150         NextEntry = NextEntry->Flink;
151         WaitEntry = WaitIrp->Tail.Overlay.DriverContext[1];
152 
153         if (WaitEntry->AliasName.Length)
154         {
155             ASSERT(FALSE);
156             /* We have an alias. Use that for comparison */
157             WaitName = WaitEntry->AliasName;
158             PipeName = PipePathUpper;
159         }
160         else
161         {
162             /* Use the name from the wait buffer to compare */
163             WaitBuffer = WaitIrp->AssociatedIrp.SystemBuffer;
164             WaitName.Buffer = WaitBuffer->Name;
165             WaitName.Length = WaitBuffer->NameLength;
166             WaitName.MaximumLength = WaitName.Length;
167 
168             /* WaitName doesn't have a leading backslash,
169              * so skip the one in PipePathUpper for the comparison */
170             PipeName.Buffer = PipePathUpper.Buffer + 1;
171             PipeName.Length = PipePathUpper.Length - sizeof(WCHAR);
172             PipeName.MaximumLength = PipeName.Length;
173         }
174 
175         /* Can't use RtlEqualUnicodeString with a spinlock held */
176         if (NpEqualUnicodeString(&WaitName, &PipeName))
177         {
178             /* Found a matching wait. Cancel it */
179             RemoveEntryList(&WaitIrp->Tail.Overlay.ListEntry);
180             if (KeCancelTimer(&WaitEntry->Timer))
181             {
182                 WaitEntry->WaitQueue = (PNP_WAIT_QUEUE)Linkage;
183                 Linkage = WaitEntry;
184             }
185             else
186             {
187                 WaitEntry->Irp = NULL;
188                 WaitIrp->Tail.Overlay.DriverContext[1] = NULL;
189             }
190 
191             if (IoSetCancelRoutine(WaitIrp, NULL))
192             {
193                 WaitIrp->IoStatus.Information = 0;
194                 WaitIrp->IoStatus.Status = Status;
195                 InsertTailList(List, &WaitIrp->Tail.Overlay.ListEntry);
196             }
197             else
198             {
199                 WaitIrp->Tail.Overlay.DriverContext[1] = NULL;
200             }
201         }
202     }
203 
204     KeReleaseSpinLock(&WaitQueue->WaitLock, OldIrql);
205 
206     ExFreePoolWithTag(Buffer, NPFS_WAIT_BLOCK_TAG);
207 
208     while (Linkage)
209     {
210         WaitEntry = Linkage;
211         Linkage = (PNP_WAIT_QUEUE_ENTRY)Linkage->WaitQueue;
212         ObDereferenceObject(WaitEntry->FileObject);
213         ExFreePool(WaitEntry);
214     }
215 
216     return STATUS_SUCCESS;
217 }
218 
219 NTSTATUS
220 NTAPI
NpAddWaiter(IN PNP_WAIT_QUEUE WaitQueue,IN LARGE_INTEGER WaitTime,IN PIRP Irp,IN PUNICODE_STRING AliasName)221 NpAddWaiter(IN PNP_WAIT_QUEUE WaitQueue,
222             IN LARGE_INTEGER WaitTime,
223             IN PIRP Irp,
224             IN PUNICODE_STRING AliasName)
225 {
226     PIO_STACK_LOCATION IoStack;
227     KIRQL OldIrql;
228     NTSTATUS Status;
229     PNP_WAIT_QUEUE_ENTRY WaitEntry;
230     PFILE_PIPE_WAIT_FOR_BUFFER WaitBuffer;
231     LARGE_INTEGER DueTime;
232     ULONG i;
233 
234     IoStack = IoGetCurrentIrpStackLocation(Irp);
235 
236     WaitEntry = ExAllocatePoolWithQuotaTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
237                                            sizeof(*WaitEntry),
238                                            NPFS_WRITE_BLOCK_TAG);
239     if (!WaitEntry)
240     {
241         return STATUS_INSUFFICIENT_RESOURCES;
242     }
243 
244     KeInitializeDpc(&WaitEntry->Dpc, NpTimerDispatch, WaitEntry);
245     KeInitializeTimer(&WaitEntry->Timer);
246 
247     if (AliasName)
248     {
249         WaitEntry->AliasName = *AliasName;
250     }
251     else
252     {
253         WaitEntry->AliasName.Length = 0;
254         WaitEntry->AliasName.Buffer = NULL;
255     }
256 
257     WaitEntry->WaitQueue = WaitQueue;
258     WaitEntry->Irp = Irp;
259 
260     WaitBuffer = Irp->AssociatedIrp.SystemBuffer;
261     if (WaitBuffer->TimeoutSpecified)
262     {
263         DueTime = WaitBuffer->Timeout;
264     }
265     else
266     {
267         DueTime = WaitTime;
268     }
269 
270     for (i = 0; i < WaitBuffer->NameLength / sizeof(WCHAR); i++)
271     {
272         WaitBuffer->Name[i] = RtlUpcaseUnicodeChar(WaitBuffer->Name[i]);
273     }
274 
275     Irp->Tail.Overlay.DriverContext[0] = WaitQueue;
276     Irp->Tail.Overlay.DriverContext[1] = WaitEntry;
277 
278     KeAcquireSpinLock(&WaitQueue->WaitLock, &OldIrql);
279 
280     IoSetCancelRoutine(Irp, NpCancelWaitQueueIrp);
281 
282     if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
283     {
284         Status = STATUS_CANCELLED;
285     }
286     else
287     {
288         InsertTailList(&WaitQueue->WaitList, &Irp->Tail.Overlay.ListEntry);
289 
290         IoMarkIrpPending(Irp);
291         Status = STATUS_PENDING;
292 
293         WaitEntry->FileObject = IoStack->FileObject;
294         ObReferenceObject(WaitEntry->FileObject);
295 
296         KeSetTimer(&WaitEntry->Timer, DueTime, &WaitEntry->Dpc);
297         WaitEntry = NULL;
298     }
299 
300     KeReleaseSpinLock(&WaitQueue->WaitLock, OldIrql);
301     if (WaitEntry) ExFreePool(WaitEntry);
302 
303     return Status;
304 }
305 
306 /* EOF */
307