Support asynchronous (aka overlapped) connect, read and write requests.
[reactos.git] / reactos / drivers / fs / np / create.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: drivers/fs/np/create.c
6 * PURPOSE: Named pipe filesystem
7 * PROGRAMMER: David Welch <welch@cwcom.net>
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ddk/ntddk.h>
13
14 #include "npfs.h"
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* FUNCTIONS *****************************************************************/
20
21 static PNPFS_PIPE
22 NpfsFindPipe(PNPFS_DEVICE_EXTENSION DeviceExt,
23 PUNICODE_STRING PipeName)
24 {
25 PLIST_ENTRY CurrentEntry;
26 PNPFS_PIPE Pipe;
27
28 CurrentEntry = DeviceExt->PipeListHead.Flink;
29 while (CurrentEntry != &DeviceExt->PipeListHead)
30 {
31 Pipe = CONTAINING_RECORD(CurrentEntry, NPFS_PIPE, PipeListEntry);
32 if (RtlCompareUnicodeString(PipeName,
33 &Pipe->PipeName,
34 TRUE) == 0)
35 {
36 DPRINT("<%wZ> = <%wZ>\n", PipeName, &Pipe->PipeName);
37 return Pipe;
38 }
39
40 CurrentEntry = CurrentEntry->Flink;
41 }
42
43 return NULL;
44 }
45
46
47 static PNPFS_FCB
48 NpfsFindListeningServerInstance(PNPFS_PIPE Pipe)
49 {
50 PLIST_ENTRY CurrentEntry;
51 PNPFS_WAITER_ENTRY Waiter;
52
53 CurrentEntry = Pipe->WaiterListHead.Flink;
54 while (CurrentEntry != &Pipe->WaiterListHead)
55 {
56 Waiter = CONTAINING_RECORD(CurrentEntry, NPFS_WAITER_ENTRY, Entry);
57 if (Waiter->Fcb->PipeState == FILE_PIPE_LISTENING_STATE)
58 {
59 DPRINT("Server found! Fcb %p\n", Waiter->Fcb);
60 return Waiter->Fcb;
61 }
62
63 CurrentEntry = CurrentEntry->Flink;
64 }
65
66 return NULL;
67 }
68
69
70 static VOID
71 NpfsSignalAndRemoveListeningServerInstance(PNPFS_PIPE Pipe,
72 PNPFS_FCB Fcb)
73 {
74 PLIST_ENTRY CurrentEntry;
75 PNPFS_WAITER_ENTRY Waiter;
76
77 CurrentEntry = Pipe->WaiterListHead.Flink;
78 while (CurrentEntry != &Pipe->WaiterListHead)
79 {
80 Waiter = CONTAINING_RECORD(CurrentEntry, NPFS_WAITER_ENTRY, Entry);
81 if (Waiter->Fcb == Fcb)
82 {
83 DPRINT("Server found! Fcb %p\n", Waiter->Fcb);
84
85 KeSetEvent(Waiter->Irp->UserEvent, 0, FALSE);
86 Waiter->Irp->UserIosb->Status = FILE_PIPE_CONNECTED_STATE;
87 Waiter->Irp->UserIosb->Information = 0;
88 IoCompleteRequest(Waiter->Irp, IO_NO_INCREMENT);
89
90 RemoveEntryList(&Waiter->Entry);
91 ExFreePool(Waiter);
92 return;
93 }
94 CurrentEntry = CurrentEntry->Flink;
95 }
96 }
97
98
99 NTSTATUS STDCALL
100 NpfsCreate(PDEVICE_OBJECT DeviceObject,
101 PIRP Irp)
102 {
103 PIO_STACK_LOCATION IoStack;
104 PFILE_OBJECT FileObject;
105 PNPFS_PIPE Pipe;
106 PNPFS_FCB ClientFcb;
107 PNPFS_FCB ServerFcb = NULL;
108 PNPFS_DEVICE_EXTENSION DeviceExt;
109 BOOLEAN SpecialAccess;
110
111 DPRINT("NpfsCreate(DeviceObject %p Irp %p)\n", DeviceObject, Irp);
112
113 DeviceExt = (PNPFS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
114 IoStack = IoGetCurrentIrpStackLocation(Irp);
115 FileObject = IoStack->FileObject;
116 DPRINT("FileObject %p\n", FileObject);
117 DPRINT("FileName %wZ\n", &FileObject->FileName);
118
119 Irp->IoStatus.Information = 0;
120
121 SpecialAccess = ((IoStack->Parameters.Create.ShareAccess & 3) == 3);
122 if (SpecialAccess)
123 {
124 DPRINT("NpfsCreate() open client end for special use!\n");
125 }
126
127 /*
128 * Step 1. Find the pipe we're trying to open.
129 */
130 KeLockMutex(&DeviceExt->PipeListLock);
131 Pipe = NpfsFindPipe(DeviceExt,
132 &FileObject->FileName);
133 if (Pipe == NULL)
134 {
135 /* Not found, bail out with error. */
136 DPRINT("No pipe found!\n");
137 KeUnlockMutex(&DeviceExt->PipeListLock);
138 Irp->IoStatus.Status = STATUS_OBJECT_NAME_NOT_FOUND;
139 IoCompleteRequest(Irp, IO_NO_INCREMENT);
140 return STATUS_OBJECT_NAME_NOT_FOUND;
141 }
142
143 KeUnlockMutex(&DeviceExt->PipeListLock);
144
145 /*
146 * Step 2. Search for listening server FCB.
147 */
148
149 /*
150 * Acquire the lock for FCB lists. From now on no modifications to the
151 * FCB lists are allowed, because it can cause various misconsistencies.
152 */
153 KeLockMutex(&Pipe->FcbListLock);
154
155 if (!SpecialAccess)
156 {
157 ServerFcb = NpfsFindListeningServerInstance(Pipe);
158 if (ServerFcb == NULL)
159 {
160 /* Not found, bail out with error for FILE_OPEN requests. */
161 DPRINT("No listening server fcb found!\n");
162 KeUnlockMutex(&Pipe->FcbListLock);
163 Irp->IoStatus.Status = STATUS_PIPE_BUSY;
164 IoCompleteRequest(Irp, IO_NO_INCREMENT);
165 return STATUS_PIPE_BUSY;
166 }
167 }
168 else if (IsListEmpty(&Pipe->ServerFcbListHead))
169 {
170 DPRINT("No server fcb found!\n");
171 KeUnlockMutex(&Pipe->FcbListLock);
172 Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
173 IoCompleteRequest(Irp, IO_NO_INCREMENT);
174 return STATUS_UNSUCCESSFUL;
175 }
176
177 /*
178 * Step 3. Create the client FCB.
179 */
180 ClientFcb = ExAllocatePool(NonPagedPool, sizeof(NPFS_FCB));
181 if (ClientFcb == NULL)
182 {
183 DPRINT("No memory!\n");
184 KeUnlockMutex(&Pipe->FcbListLock);
185 Irp->IoStatus.Status = STATUS_NO_MEMORY;
186 IoCompleteRequest(Irp, IO_NO_INCREMENT);
187 return STATUS_NO_MEMORY;
188 }
189
190 ClientFcb->Thread = (struct ETHREAD *)Irp->Tail.Overlay.Thread;
191 ClientFcb->Pipe = Pipe;
192 ClientFcb->PipeEnd = FILE_PIPE_CLIENT_END;
193 ClientFcb->OtherSide = NULL;
194 ClientFcb->PipeState = SpecialAccess ? 0 : FILE_PIPE_DISCONNECTED_STATE;
195
196 /* Initialize data list. */
197 if (Pipe->OutboundQuota)
198 {
199 ClientFcb->Data = ExAllocatePool(NonPagedPool, Pipe->OutboundQuota);
200 if (ClientFcb->Data == NULL)
201 {
202 DPRINT("No memory!\n");
203 ExFreePool(ClientFcb);
204 KeUnlockMutex(&Pipe->FcbListLock);
205 Irp->IoStatus.Status = STATUS_NO_MEMORY;
206 IoCompleteRequest(Irp, IO_NO_INCREMENT);
207 return STATUS_NO_MEMORY;
208 }
209 }
210 else
211 {
212 ClientFcb->Data = NULL;
213 }
214
215 ClientFcb->ReadPtr = ClientFcb->Data;
216 ClientFcb->WritePtr = ClientFcb->Data;
217 ClientFcb->ReadDataAvailable = 0;
218 ClientFcb->WriteQuotaAvailable = Pipe->OutboundQuota;
219 ClientFcb->MaxDataLength = Pipe->OutboundQuota;
220 KeInitializeSpinLock(&ClientFcb->DataListLock);
221 KeInitializeEvent(&ClientFcb->ConnectEvent, SynchronizationEvent, FALSE);
222 KeInitializeEvent(&ClientFcb->Event, SynchronizationEvent, FALSE);
223
224 /*
225 * Step 4. Add the client FCB to a list and connect it if possible.
226 */
227
228 /* Add the client FCB to the pipe FCB list. */
229 InsertTailList(&Pipe->ClientFcbListHead, &ClientFcb->FcbListEntry);
230
231 /* Connect to listening server side */
232 if (ServerFcb)
233 {
234 ClientFcb->OtherSide = ServerFcb;
235 ServerFcb->OtherSide = ClientFcb;
236 ClientFcb->PipeState = FILE_PIPE_CONNECTED_STATE;
237 ServerFcb->PipeState = FILE_PIPE_CONNECTED_STATE;
238
239 /* Signal the server thread and remove it from the waiter list */
240 NpfsSignalAndRemoveListeningServerInstance(Pipe, ServerFcb);
241 }
242
243 KeUnlockMutex(&Pipe->FcbListLock);
244
245 FileObject->FsContext = ClientFcb;
246
247 Irp->IoStatus.Status = STATUS_SUCCESS;
248 IoCompleteRequest(Irp, IO_NO_INCREMENT);
249
250 DPRINT("Success!\n");
251
252 return STATUS_SUCCESS;
253 }
254
255
256 NTSTATUS STDCALL
257 NpfsCreateNamedPipe(PDEVICE_OBJECT DeviceObject,
258 PIRP Irp)
259 {
260 PIO_STACK_LOCATION IoStack;
261 PFILE_OBJECT FileObject;
262 PNPFS_DEVICE_EXTENSION DeviceExt;
263 PNPFS_PIPE Pipe;
264 PNPFS_FCB Fcb;
265 PNAMED_PIPE_CREATE_PARAMETERS Buffer;
266 BOOLEAN NewPipe = FALSE;
267
268 DPRINT("NpfsCreateNamedPipe(DeviceObject %p Irp %p)\n", DeviceObject, Irp);
269
270 DeviceExt = (PNPFS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
271 IoStack = IoGetCurrentIrpStackLocation(Irp);
272 FileObject = IoStack->FileObject;
273 DPRINT("FileObject %p\n", FileObject);
274 DPRINT("Pipe name %wZ\n", &FileObject->FileName);
275
276 Buffer = IoStack->Parameters.CreatePipe.Parameters;
277
278 Irp->IoStatus.Information = 0;
279
280 Fcb = ExAllocatePool(NonPagedPool, sizeof(NPFS_FCB));
281 if (Fcb == NULL)
282 {
283 Irp->IoStatus.Status = STATUS_NO_MEMORY;
284 IoCompleteRequest(Irp, IO_NO_INCREMENT);
285 return STATUS_NO_MEMORY;
286 }
287
288 Fcb->Thread = (struct ETHREAD *)Irp->Tail.Overlay.Thread;
289 KeLockMutex(&DeviceExt->PipeListLock);
290
291 /*
292 * First search for existing Pipe with the same name.
293 */
294 Pipe = NpfsFindPipe(DeviceExt,
295 &FileObject->FileName);
296 if (Pipe != NULL)
297 {
298 /*
299 * Found Pipe with the same name. Check if we are
300 * allowed to use it.
301 */
302 KeUnlockMutex(&DeviceExt->PipeListLock);
303
304 if (Pipe->CurrentInstances >= Pipe->MaximumInstances)
305 {
306 DPRINT("Out of instances.\n");
307 ExFreePool(Fcb);
308 Irp->IoStatus.Status = STATUS_PIPE_BUSY;
309 IoCompleteRequest(Irp, IO_NO_INCREMENT);
310 return STATUS_PIPE_BUSY;
311 }
312
313 /* FIXME: Check pipe modes also! */
314 if (Pipe->MaximumInstances != Buffer->MaximumInstances ||
315 Pipe->TimeOut.QuadPart != Buffer->DefaultTimeout.QuadPart)
316 {
317 DPRINT("Asked for invalid pipe mode.\n");
318 ExFreePool(Fcb);
319 Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
320 IoCompleteRequest(Irp, IO_NO_INCREMENT);
321 return STATUS_ACCESS_DENIED;
322 }
323 }
324 else
325 {
326 NewPipe = TRUE;
327 Pipe = ExAllocatePool(NonPagedPool, sizeof(NPFS_PIPE));
328 if (Pipe == NULL)
329 {
330 KeUnlockMutex(&DeviceExt->PipeListLock);
331 Irp->IoStatus.Status = STATUS_NO_MEMORY;
332 Irp->IoStatus.Information = 0;
333 IoCompleteRequest(Irp, IO_NO_INCREMENT);
334 return STATUS_NO_MEMORY;
335 }
336
337 if (RtlCreateUnicodeString(&Pipe->PipeName, FileObject->FileName.Buffer) == FALSE)
338 {
339 KeUnlockMutex(&DeviceExt->PipeListLock);
340 ExFreePool(Pipe);
341 ExFreePool(Fcb);
342 Irp->IoStatus.Status = STATUS_NO_MEMORY;
343 Irp->IoStatus.Information = 0;
344 IoCompleteRequest(Irp, IO_NO_INCREMENT);
345 return STATUS_NO_MEMORY;
346 }
347
348 InitializeListHead(&Pipe->ServerFcbListHead);
349 InitializeListHead(&Pipe->ClientFcbListHead);
350 InitializeListHead(&Pipe->WaiterListHead);
351 KeInitializeMutex(&Pipe->FcbListLock, 0);
352
353 Pipe->PipeType = Buffer->NamedPipeType;
354 Pipe->WriteMode = Buffer->ReadMode;
355 Pipe->ReadMode = Buffer->ReadMode;
356 Pipe->CompletionMode = Buffer->CompletionMode;
357 Pipe->PipeConfiguration = IoStack->Parameters.CreatePipe.Options & 0x3;
358 Pipe->MaximumInstances = Buffer->MaximumInstances;
359 Pipe->CurrentInstances = 0;
360 Pipe->TimeOut = Buffer->DefaultTimeout;
361 if (!(IoStack->Parameters.Create.Options & FILE_PIPE_OUTBOUND) ||
362 IoStack->Parameters.Create.Options & FILE_PIPE_FULL_DUPLEX)
363 {
364 if (Buffer->InboundQuota == 0)
365 {
366 Pipe->InboundQuota = DeviceExt->DefaultQuota;
367 }
368 else
369 {
370 Pipe->InboundQuota = PAGE_ROUND_UP(Buffer->InboundQuota);
371 if (Pipe->InboundQuota < DeviceExt->MinQuota)
372 {
373 Pipe->InboundQuota = DeviceExt->MinQuota;
374 }
375 else if (Pipe->InboundQuota > DeviceExt->MaxQuota)
376 {
377 Pipe->InboundQuota = DeviceExt->MaxQuota;
378 }
379 }
380 }
381 else
382 {
383 Pipe->InboundQuota = 0;
384 }
385
386 if (IoStack->Parameters.Create.Options & (FILE_PIPE_FULL_DUPLEX|FILE_PIPE_OUTBOUND))
387 {
388 if (Buffer->OutboundQuota == 0)
389 {
390 Pipe->OutboundQuota = DeviceExt->DefaultQuota;
391 }
392 else
393 {
394 Pipe->OutboundQuota = PAGE_ROUND_UP(Buffer->OutboundQuota);
395 if (Pipe->OutboundQuota < DeviceExt->MinQuota)
396 {
397 Pipe->OutboundQuota = DeviceExt->MinQuota;
398 }
399 else if (Pipe->OutboundQuota > DeviceExt->MaxQuota)
400 {
401 Pipe->OutboundQuota = DeviceExt->MaxQuota;
402 }
403 }
404 }
405 else
406 {
407 Pipe->OutboundQuota = 0;
408 }
409
410 InsertTailList(&DeviceExt->PipeListHead, &Pipe->PipeListEntry);
411 KeUnlockMutex(&DeviceExt->PipeListLock);
412 }
413
414 if (Pipe->InboundQuota)
415 {
416 Fcb->Data = ExAllocatePool(NonPagedPool, Pipe->InboundQuota);
417 if (Fcb->Data == NULL)
418 {
419 ExFreePool(Fcb);
420
421 if (NewPipe)
422 {
423 RtlFreeUnicodeString(&Pipe->PipeName);
424 ExFreePool(Pipe);
425 }
426
427 Irp->IoStatus.Status = STATUS_NO_MEMORY;
428 IoCompleteRequest(Irp, IO_NO_INCREMENT);
429 return STATUS_NO_MEMORY;
430 }
431 }
432 else
433 {
434 Fcb->Data = NULL;
435 }
436
437 Fcb->ReadPtr = Fcb->Data;
438 Fcb->WritePtr = Fcb->Data;
439 Fcb->ReadDataAvailable = 0;
440 Fcb->WriteQuotaAvailable = Pipe->InboundQuota;
441 Fcb->MaxDataLength = Pipe->InboundQuota;
442 KeInitializeSpinLock(&Fcb->DataListLock);
443
444 Pipe->CurrentInstances++;
445
446 KeLockMutex(&Pipe->FcbListLock);
447 InsertTailList(&Pipe->ServerFcbListHead, &Fcb->FcbListEntry);
448 KeUnlockMutex(&Pipe->FcbListLock);
449
450 Fcb->Pipe = Pipe;
451 Fcb->PipeEnd = FILE_PIPE_SERVER_END;
452 Fcb->PipeState = FILE_PIPE_LISTENING_STATE;
453 Fcb->OtherSide = NULL;
454
455 KeInitializeEvent(&Fcb->ConnectEvent,
456 SynchronizationEvent,
457 FALSE);
458
459 KeInitializeEvent(&Fcb->Event,
460 SynchronizationEvent,
461 FALSE);
462
463 FileObject->FsContext = Fcb;
464
465 Irp->IoStatus.Status = STATUS_SUCCESS;
466 IoCompleteRequest(Irp, IO_NO_INCREMENT);
467
468 DPRINT("Success!\n");
469
470 return STATUS_SUCCESS;
471 }
472
473
474 NTSTATUS STDCALL
475 NpfsClose(PDEVICE_OBJECT DeviceObject,
476 PIRP Irp)
477 {
478 PNPFS_DEVICE_EXTENSION DeviceExt;
479 PIO_STACK_LOCATION IoStack;
480 PFILE_OBJECT FileObject;
481 PNPFS_FCB Fcb;
482 PNPFS_PIPE Pipe;
483 BOOL Server;
484
485 DPRINT("NpfsClose(DeviceObject %p Irp %p)\n", DeviceObject, Irp);
486
487 IoStack = IoGetCurrentIrpStackLocation(Irp);
488 DeviceExt = (PNPFS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
489 FileObject = IoStack->FileObject;
490 Fcb = FileObject->FsContext;
491
492 if (Fcb == NULL)
493 {
494 DPRINT("Success!\n");
495 Irp->IoStatus.Status = STATUS_SUCCESS;
496 Irp->IoStatus.Information = 0;
497 IoCompleteRequest(Irp, IO_NO_INCREMENT);
498 return STATUS_SUCCESS;
499 }
500
501 DPRINT("Fcb %x\n", Fcb);
502 Pipe = Fcb->Pipe;
503
504 DPRINT("Closing pipe %wZ\n", &Pipe->PipeName);
505
506 KeLockMutex(&Pipe->FcbListLock);
507
508 Server = (Fcb->PipeEnd == FILE_PIPE_SERVER_END);
509
510 if (Server)
511 {
512 /* FIXME: Clean up existing connections here ?? */
513 DPRINT("Server\n");
514 Pipe->CurrentInstances--;
515 }
516 else
517 {
518 DPRINT("Client\n");
519 }
520
521 if (Fcb->PipeState == FILE_PIPE_CONNECTED_STATE)
522 {
523 if (Fcb->OtherSide)
524 {
525 Fcb->OtherSide->PipeState = FILE_PIPE_CLOSING_STATE;
526 Fcb->OtherSide->OtherSide = NULL;
527 /*
528 * Signaling the write event. If is possible that an other
529 * thread waits for an empty buffer.
530 */
531 KeSetEvent(&Fcb->OtherSide->Event, IO_NO_INCREMENT, FALSE);
532 }
533
534 Fcb->PipeState = 0;
535 }
536
537 FileObject->FsContext = NULL;
538
539 RemoveEntryList(&Fcb->FcbListEntry);
540 if (Fcb->Data)
541 ExFreePool(Fcb->Data);
542 ExFreePool(Fcb);
543
544 KeUnlockMutex(&Pipe->FcbListLock);
545
546 if (IsListEmpty(&Pipe->ServerFcbListHead) &&
547 IsListEmpty(&Pipe->ClientFcbListHead))
548 {
549 RtlFreeUnicodeString(&Pipe->PipeName);
550 KeLockMutex(&DeviceExt->PipeListLock);
551 RemoveEntryList(&Pipe->PipeListEntry);
552 KeUnlockMutex(&DeviceExt->PipeListLock);
553 ExFreePool(Pipe);
554 }
555
556 Irp->IoStatus.Status = STATUS_SUCCESS;
557 Irp->IoStatus.Information = 0;
558 IoCompleteRequest(Irp, IO_NO_INCREMENT);
559
560 DPRINT("Success!\n");
561
562 return STATUS_SUCCESS;
563 }
564
565 /* EOF */