Sync with trunk (r48008)
[reactos.git] / drivers / filesystems / npfs / fsctrl.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/np/fsctrl.c
5 * PURPOSE: Named pipe filesystem
6 * PROGRAMMER: David Welch <welch@cwcom.net>
7 * Eric Kohl
8 * Michael Martin
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include "npfs.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* FUNCTIONS *****************************************************************/
19
20 static DRIVER_CANCEL NpfsListeningCancelRoutine;
21 static VOID NTAPI
22 NpfsListeningCancelRoutine(IN PDEVICE_OBJECT DeviceObject,
23 IN PIRP Irp)
24 {
25 PNPFS_WAITER_ENTRY Waiter;
26
27 Waiter = (PNPFS_WAITER_ENTRY)&Irp->Tail.Overlay.DriverContext;
28
29 DPRINT("NpfsListeningCancelRoutine() called for <%wZ>\n",
30 &Waiter->Ccb->Fcb->PipeName);
31
32 IoReleaseCancelSpinLock(Irp->CancelIrql);
33
34
35 KeLockMutex(&Waiter->Ccb->Fcb->CcbListLock);
36 RemoveEntryList(&Waiter->Entry);
37 KeUnlockMutex(&Waiter->Ccb->Fcb->CcbListLock);
38
39 Irp->IoStatus.Status = STATUS_CANCELLED;
40 Irp->IoStatus.Information = 0;
41 IoCompleteRequest(Irp, IO_NO_INCREMENT);
42 }
43
44
45 static NTSTATUS
46 NpfsAddListeningServerInstance(PIRP Irp,
47 PNPFS_CCB Ccb)
48 {
49 PNPFS_WAITER_ENTRY Entry;
50 KIRQL oldIrql;
51
52 Entry = (PNPFS_WAITER_ENTRY)&Irp->Tail.Overlay.DriverContext;
53
54 Entry->Ccb = Ccb;
55
56 KeLockMutex(&Ccb->Fcb->CcbListLock);
57
58 IoMarkIrpPending(Irp);
59 InsertTailList(&Ccb->Fcb->WaiterListHead, &Entry->Entry);
60
61 IoAcquireCancelSpinLock(&oldIrql);
62 if (!Irp->Cancel)
63 {
64 (void)IoSetCancelRoutine(Irp, NpfsListeningCancelRoutine);
65 IoReleaseCancelSpinLock(oldIrql);
66 KeUnlockMutex(&Ccb->Fcb->CcbListLock);
67 return STATUS_PENDING;
68 }
69 IoReleaseCancelSpinLock(oldIrql);
70
71 RemoveEntryList(&Entry->Entry);
72
73 Irp->IoStatus.Status = STATUS_CANCELLED;
74 Irp->IoStatus.Information = 0;
75 IoCompleteRequest(Irp, IO_NO_INCREMENT);
76 KeUnlockMutex(&Ccb->Fcb->CcbListLock);
77
78 return STATUS_CANCELLED;
79 }
80
81
82 static NTSTATUS
83 NpfsConnectPipe(PIRP Irp,
84 PNPFS_CCB Ccb)
85 {
86 PIO_STACK_LOCATION IoStack;
87 PFILE_OBJECT FileObject;
88 ULONG Flags;
89 PLIST_ENTRY current_entry;
90 PNPFS_FCB Fcb;
91 PNPFS_CCB ClientCcb;
92 NTSTATUS Status;
93
94 DPRINT("NpfsConnectPipe()\n");
95
96 if (Ccb->PipeState == FILE_PIPE_CONNECTED_STATE)
97 {
98 KeResetEvent(&Ccb->ConnectEvent);
99 return STATUS_PIPE_CONNECTED;
100 }
101
102 if (Ccb->PipeState == FILE_PIPE_CLOSING_STATE)
103 return STATUS_PIPE_CLOSING;
104
105 DPRINT("Waiting for connection...\n");
106
107 Fcb = Ccb->Fcb;
108 IoStack = IoGetCurrentIrpStackLocation(Irp);
109 FileObject = IoStack->FileObject;
110 Flags = FileObject->Flags;
111
112 /* search for a listening client fcb */
113 KeLockMutex(&Fcb->CcbListLock);
114
115 current_entry = Fcb->ClientCcbListHead.Flink;
116 while (current_entry != &Fcb->ClientCcbListHead)
117 {
118 ClientCcb = CONTAINING_RECORD(current_entry,
119 NPFS_CCB,
120 CcbListEntry);
121
122 if (ClientCcb->PipeState == 0)
123 {
124 /* found a passive (waiting) client CCB */
125 DPRINT("Passive (waiting) client CCB found -- wake the client\n");
126 KeSetEvent(&ClientCcb->ConnectEvent, IO_NO_INCREMENT, FALSE);
127 break;
128 }
129
130 #if 0
131 if (ClientCcb->PipeState == FILE_PIPE_LISTENING_STATE)
132 {
133 /* found a listening client CCB */
134 DPRINT("Listening client CCB found -- connecting\n");
135
136 /* connect client and server CCBs */
137 Ccb->OtherSide = ClientCcb;
138 ClientCcb->OtherSide = Ccb;
139
140 /* set connected state */
141 Ccb->PipeState = FILE_PIPE_CONNECTED_STATE;
142 ClientCcb->PipeState = FILE_PIPE_CONNECTED_STATE;
143
144 KeUnlockMutex(&Fcb->CcbListLock);
145
146 /* FIXME: create and initialize data queues */
147
148 /* signal client's connect event */
149 DPRINT("Setting the ConnectEvent for %x\n", ClientCcb);
150 KeSetEvent(&ClientCcb->ConnectEvent, IO_NO_INCREMENT, FALSE);
151
152 return STATUS_PIPE_CONNECTED;
153 }
154 #endif
155
156 current_entry = current_entry->Flink;
157 }
158
159 /* no listening client fcb found */
160 DPRINT("No listening client fcb found -- waiting for client\n");
161
162 Ccb->PipeState = FILE_PIPE_LISTENING_STATE;
163
164 Status = NpfsAddListeningServerInstance(Irp, Ccb);
165
166 KeUnlockMutex(&Fcb->CcbListLock);
167
168 if (Flags & FO_SYNCHRONOUS_IO)
169 {
170 KeWaitForSingleObject(&Ccb->ConnectEvent,
171 UserRequest,
172 Irp->RequestorMode,
173 FALSE,
174 NULL);
175 }
176
177 DPRINT("NpfsConnectPipe() done (Status %lx)\n", Status);
178
179 return Status;
180 }
181
182
183 static NTSTATUS
184 NpfsDisconnectPipe(PNPFS_CCB Ccb)
185 {
186 NTSTATUS Status;
187 PNPFS_FCB Fcb;
188 PNPFS_CCB OtherSide;
189 BOOLEAN Server;
190
191 DPRINT("NpfsDisconnectPipe()\n");
192
193 Fcb = Ccb->Fcb;
194 KeLockMutex(&Fcb->CcbListLock);
195
196 if (Ccb->PipeState == FILE_PIPE_DISCONNECTED_STATE)
197 {
198 DPRINT("Pipe is already disconnected\n");
199 Status = STATUS_PIPE_DISCONNECTED;
200 }
201 else if ((!Ccb->OtherSide) && (Ccb->PipeState == FILE_PIPE_CONNECTED_STATE))
202 {
203 ExAcquireFastMutex(&Ccb->DataListLock);
204 Ccb->PipeState = FILE_PIPE_DISCONNECTED_STATE;
205 ExReleaseFastMutex(&Ccb->DataListLock);
206 Status = STATUS_SUCCESS;
207 }
208 else if (Ccb->PipeState == FILE_PIPE_CONNECTED_STATE)
209 {
210 Server = (Ccb->PipeEnd == FILE_PIPE_SERVER_END);
211 OtherSide = Ccb->OtherSide;
212 //Ccb->OtherSide = NULL;
213 Ccb->PipeState = FILE_PIPE_DISCONNECTED_STATE;
214 /* Lock the server first */
215 if (Server)
216 {
217 ExAcquireFastMutex(&Ccb->DataListLock);
218 ExAcquireFastMutex(&OtherSide->DataListLock);
219 }
220 else
221 {
222 ExAcquireFastMutex(&OtherSide->DataListLock);
223 ExAcquireFastMutex(&Ccb->DataListLock);
224 }
225 OtherSide->PipeState = FILE_PIPE_DISCONNECTED_STATE;
226 //OtherSide->OtherSide = NULL;
227 /*
228 * Signaling the write event. If is possible that an other
229 * thread waits for an empty buffer.
230 */
231 KeSetEvent(&OtherSide->ReadEvent, IO_NO_INCREMENT, FALSE);
232 KeSetEvent(&OtherSide->WriteEvent, IO_NO_INCREMENT, FALSE);
233 if (Server)
234 {
235 ExReleaseFastMutex(&OtherSide->DataListLock);
236 ExReleaseFastMutex(&Ccb->DataListLock);
237 }
238 else
239 {
240 ExReleaseFastMutex(&Ccb->DataListLock);
241 ExReleaseFastMutex(&OtherSide->DataListLock);
242 }
243 Status = STATUS_SUCCESS;
244 }
245 else if (Ccb->PipeState == FILE_PIPE_LISTENING_STATE)
246 {
247 PLIST_ENTRY Entry;
248 PNPFS_WAITER_ENTRY WaitEntry = NULL;
249 BOOLEAN Complete = FALSE;
250 PIRP Irp = NULL;
251
252 Entry = Ccb->Fcb->WaiterListHead.Flink;
253 while (Entry != &Ccb->Fcb->WaiterListHead)
254 {
255 WaitEntry = CONTAINING_RECORD(Entry, NPFS_WAITER_ENTRY, Entry);
256 if (WaitEntry->Ccb == Ccb)
257 {
258 RemoveEntryList(Entry);
259 Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DriverContext);
260 Complete = (NULL == IoSetCancelRoutine(Irp, NULL));
261 break;
262 }
263 Entry = Entry->Flink;
264 }
265
266 if (Irp)
267 {
268 if (Complete)
269 {
270 Irp->IoStatus.Status = STATUS_PIPE_BROKEN;
271 Irp->IoStatus.Information = 0;
272 IoCompleteRequest(Irp, IO_NO_INCREMENT);
273 }
274 }
275 Ccb->PipeState = FILE_PIPE_DISCONNECTED_STATE;
276 Status = STATUS_SUCCESS;
277 }
278 else if (Ccb->PipeState == FILE_PIPE_CLOSING_STATE)
279 {
280 Status = STATUS_PIPE_CLOSING;
281 }
282 else
283 {
284 Status = STATUS_UNSUCCESSFUL;
285 }
286 KeUnlockMutex(&Fcb->CcbListLock);
287 return Status;
288 }
289
290
291 static NTSTATUS
292 NpfsWaitPipe(PIRP Irp,
293 PNPFS_CCB Ccb)
294 {
295 PLIST_ENTRY current_entry;
296 PNPFS_FCB Fcb;
297 PNPFS_CCB ServerCcb;
298 PFILE_PIPE_WAIT_FOR_BUFFER WaitPipe;
299 NTSTATUS Status;
300 LARGE_INTEGER TimeOut;
301
302 DPRINT("NpfsWaitPipe\n");
303
304 WaitPipe = (PFILE_PIPE_WAIT_FOR_BUFFER)Irp->AssociatedIrp.SystemBuffer;
305 Fcb = Ccb->Fcb;
306
307 if (Ccb->PipeState != 0)
308 {
309 DPRINT("Pipe is not in passive (waiting) state!\n");
310 return STATUS_UNSUCCESSFUL;
311 }
312
313 /* search for listening server */
314 current_entry = Fcb->ServerCcbListHead.Flink;
315 while (current_entry != &Fcb->ServerCcbListHead)
316 {
317 ServerCcb = CONTAINING_RECORD(current_entry,
318 NPFS_CCB,
319 CcbListEntry);
320
321 if (ServerCcb->PipeState == FILE_PIPE_LISTENING_STATE)
322 {
323 /* found a listening server CCB */
324 DPRINT("Listening server CCB found -- connecting\n");
325
326 return STATUS_SUCCESS;
327 }
328
329 current_entry = current_entry->Flink;
330 }
331
332 /* No listening server fcb found */
333
334 /* If no timeout specified, use the default one */
335 if (WaitPipe->TimeoutSpecified)
336 TimeOut = WaitPipe->Timeout;
337 else
338 TimeOut = Fcb->TimeOut;
339
340 /* Wait for one */
341 Status = KeWaitForSingleObject(&Ccb->ConnectEvent,
342 UserRequest,
343 KernelMode,
344 FALSE,
345 &TimeOut);
346
347 DPRINT("KeWaitForSingleObject() returned (Status %lx)\n", Status);
348
349 return Status;
350 }
351
352
353 /*
354 * FUNCTION: Return current state of a pipe
355 * ARGUMENTS:
356 * Irp = Pointer to I/O request packet
357 * IrpSp = Pointer to current stack location of Irp
358 * RETURNS:
359 * Status of operation
360 */
361
362 /*
363 * FUNCTION: Peek at a pipe (get information about messages)
364 * ARGUMENTS:
365 * Irp = Pointer to I/O request packet
366 * IoStack = Pointer to current stack location of Irp
367 * RETURNS:
368 * Status of operation
369 */
370 static NTSTATUS
371 NpfsPeekPipe(PIRP Irp,
372 PIO_STACK_LOCATION IoStack)
373 {
374 ULONG OutputBufferLength;
375 ULONG ReturnLength = 0;
376 PFILE_PIPE_PEEK_BUFFER Reply;
377 PNPFS_FCB Fcb;
378 PNPFS_CCB Ccb;
379 NTSTATUS Status;
380 ULONG MessageCount = 0;
381 ULONG MessageLength;
382 ULONG ReadDataAvailable;
383 PVOID BufferPtr;
384
385 DPRINT("NpfsPeekPipe\n");
386
387 OutputBufferLength = IoStack->Parameters.DeviceIoControl.OutputBufferLength;
388 DPRINT("OutputBufferLength: %lu\n", OutputBufferLength);
389
390 /* Validate parameters */
391 if (OutputBufferLength < sizeof(FILE_PIPE_PEEK_BUFFER))
392 {
393 DPRINT1("Buffer too small\n");
394 return STATUS_INVALID_PARAMETER;
395 }
396
397 Ccb = IoStack->FileObject->FsContext2;
398 Reply = (PFILE_PIPE_PEEK_BUFFER)Irp->AssociatedIrp.SystemBuffer;
399 Fcb = Ccb->Fcb;
400
401
402 Reply->NamedPipeState = Ccb->PipeState;
403
404 Reply->ReadDataAvailable = Ccb->ReadDataAvailable;
405 DPRINT("ReadDataAvailable: %lu\n", Ccb->ReadDataAvailable);
406
407 ExAcquireFastMutex(&Ccb->DataListLock);
408 BufferPtr = Ccb->ReadPtr;
409 DPRINT("BufferPtr = %x\n", BufferPtr);
410 if (Ccb->Fcb->PipeType == FILE_PIPE_BYTE_STREAM_TYPE)
411 {
412 DPRINT("Byte Stream Mode\n");
413 Reply->MessageLength = Ccb->ReadDataAvailable;
414 DPRINT("Reply->MessageLength %lu\n",Reply->MessageLength );
415 MessageCount = 1;
416
417 if (Reply->Data[0] && (OutputBufferLength >= Ccb->ReadDataAvailable + FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0])))
418 {
419 ReturnLength = Ccb->ReadDataAvailable;
420 memcpy(&Reply->Data[0], (PVOID)BufferPtr, Ccb->ReadDataAvailable);
421 }
422 }
423 else
424 {
425 DPRINT("Message Mode\n");
426 ReadDataAvailable=Ccb->ReadDataAvailable;
427
428 if (ReadDataAvailable > 0)
429 {
430 memcpy(&Reply->MessageLength, BufferPtr, sizeof(ULONG));
431
432 while ((ReadDataAvailable > 0) && (BufferPtr < Ccb->WritePtr))
433 {
434 memcpy(&MessageLength, BufferPtr, sizeof(MessageLength));
435
436 ASSERT(MessageLength > 0);
437
438 DPRINT("MessageLength = %lu\n",MessageLength);
439 ReadDataAvailable -= MessageLength;
440 MessageCount++;
441
442 /* If its the first message, copy the Message if the size of buffer is large enough */
443 if (MessageCount==1)
444 {
445 if ((Reply->Data[0])
446 && (OutputBufferLength >= (MessageLength + FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]))))
447 {
448 memcpy(&Reply->Data[0], (PVOID)((ULONG_PTR)BufferPtr + sizeof(MessageLength)), MessageLength);
449 ReturnLength = MessageLength;
450 }
451 }
452
453 BufferPtr =(PVOID)((ULONG_PTR)BufferPtr + MessageLength + sizeof(MessageLength));
454 DPRINT("BufferPtr = %x\n", BufferPtr);
455 DPRINT("ReadDataAvailable: %lu\n", ReadDataAvailable);
456 }
457
458 if (ReadDataAvailable != 0)
459 {
460 DPRINT1("Possible memory corruption.\n");
461 ASSERT(FALSE);
462 }
463 }
464 }
465 ExReleaseFastMutex(&Ccb->DataListLock);
466
467 Reply->NumberOfMessages = MessageCount;
468
469 Irp->IoStatus.Information = ReturnLength + FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]);
470 Irp->IoStatus.Status = STATUS_SUCCESS;
471
472 Status = STATUS_SUCCESS;
473
474 DPRINT("NpfsPeekPipe done\n");
475
476 return Status;
477 }
478
479
480 NTSTATUS NTAPI
481 NpfsFileSystemControl(PDEVICE_OBJECT DeviceObject,
482 PIRP Irp)
483 {
484 PIO_STACK_LOCATION IoStack;
485 PFILE_OBJECT FileObject;
486 NTSTATUS Status;
487 PNPFS_DEVICE_EXTENSION DeviceExt;
488 PNPFS_FCB Fcb;
489 PNPFS_CCB Ccb;
490
491 DPRINT("NpfsFileSystemContol(DeviceObject %p Irp %p)\n", DeviceObject, Irp);
492
493 DeviceExt = (PNPFS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
494 IoStack = IoGetCurrentIrpStackLocation(Irp);
495 DPRINT("IoStack: %p\n", IoStack);
496 FileObject = IoStack->FileObject;
497 DPRINT("FileObject: %p\n", FileObject);
498 Ccb = FileObject->FsContext2;
499 DPRINT("CCB: %p\n", Ccb);
500 Fcb = Ccb->Fcb;
501 DPRINT("Pipe: %p\n", Fcb);
502 DPRINT("PipeName: %wZ\n", &Fcb->PipeName);
503
504 Irp->IoStatus.Information = 0;
505
506 switch (IoStack->Parameters.FileSystemControl.FsControlCode)
507 {
508 case FSCTL_PIPE_ASSIGN_EVENT:
509 DPRINT1("Assign event not implemented\n");
510 Status = STATUS_NOT_IMPLEMENTED;
511 break;
512
513 case FSCTL_PIPE_DISCONNECT:
514 DPRINT("Disconnecting pipe %wZ\n", &Fcb->PipeName);
515 Status = NpfsDisconnectPipe(Ccb);
516 break;
517
518 case FSCTL_PIPE_LISTEN:
519 DPRINT("Connecting pipe %wZ\n", &Fcb->PipeName);
520 Status = NpfsConnectPipe(Irp, Ccb);
521 break;
522
523 case FSCTL_PIPE_PEEK:
524 DPRINT("Peeking pipe %wZ\n", &Fcb->PipeName);
525 Status = NpfsPeekPipe(Irp, (PIO_STACK_LOCATION)IoStack);
526 break;
527
528 case FSCTL_PIPE_QUERY_EVENT:
529 DPRINT1("Query event not implemented\n");
530 Status = STATUS_NOT_IMPLEMENTED;
531 break;
532
533 case FSCTL_PIPE_TRANSCEIVE:
534 /* If you implement this, please remove the workaround in
535 lib/kernel32/file/npipe.c function TransactNamedPipe() */
536 DPRINT1("Transceive not implemented\n");
537 Status = STATUS_NOT_IMPLEMENTED;
538 break;
539
540 case FSCTL_PIPE_WAIT:
541 DPRINT("Waiting for pipe %wZ\n", &Fcb->PipeName);
542 Status = NpfsWaitPipe(Irp, Ccb);
543 break;
544
545 case FSCTL_PIPE_IMPERSONATE:
546 DPRINT1("Impersonate not implemented\n");
547 Status = STATUS_NOT_IMPLEMENTED;
548 break;
549
550 case FSCTL_PIPE_SET_CLIENT_PROCESS:
551 DPRINT1("Set client process not implemented\n");
552 Status = STATUS_NOT_IMPLEMENTED;
553 break;
554
555 case FSCTL_PIPE_QUERY_CLIENT_PROCESS:
556 DPRINT1("Query client process not implemented\n");
557 Status = STATUS_NOT_IMPLEMENTED;
558 break;
559
560 case FSCTL_PIPE_INTERNAL_READ:
561 DPRINT1("Internal read not implemented\n");
562 Status = STATUS_NOT_IMPLEMENTED;
563 break;
564
565 case FSCTL_PIPE_INTERNAL_WRITE:
566 DPRINT1("Internal write not implemented\n");
567 Status = STATUS_NOT_IMPLEMENTED;
568 break;
569
570 case FSCTL_PIPE_INTERNAL_TRANSCEIVE:
571 DPRINT1("Internal transceive not implemented\n");
572 Status = STATUS_NOT_IMPLEMENTED;
573 break;
574
575 case FSCTL_PIPE_INTERNAL_READ_OVFLOW:
576 DPRINT1("Internal read overflow not implemented\n");
577 Status = STATUS_NOT_IMPLEMENTED;
578 break;
579
580 default:
581 DPRINT1("Unrecognized IoControlCode: %x\n",
582 IoStack->Parameters.FileSystemControl.FsControlCode);
583 Status = STATUS_UNSUCCESSFUL;
584 }
585
586 if (Status != STATUS_PENDING)
587 {
588 Irp->IoStatus.Status = Status;
589
590 IoCompleteRequest(Irp, IO_NO_INCREMENT);
591 }
592
593 return Status;
594 }
595
596
597 NTSTATUS NTAPI
598 NpfsFlushBuffers(PDEVICE_OBJECT DeviceObject,
599 PIRP Irp)
600 {
601 /* FIXME: Implement */
602
603 Irp->IoStatus.Status = STATUS_SUCCESS;
604 Irp->IoStatus.Information = 0;
605
606 IoCompleteRequest(Irp, IO_NO_INCREMENT);
607
608 return STATUS_SUCCESS;
609 }
610
611 /* EOF */