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>
11 /* INCLUDES ******************************************************************/
18 //#define USING_PROPER_NPFS_WAIT_SEMANTICS
20 /* FUNCTIONS *****************************************************************/
22 static DRIVER_CANCEL NpfsListeningCancelRoutine
;
24 NpfsListeningCancelRoutine(IN PDEVICE_OBJECT DeviceObject
,
27 PNPFS_WAITER_ENTRY Waiter
;
29 Waiter
= (PNPFS_WAITER_ENTRY
)&Irp
->Tail
.Overlay
.DriverContext
;
31 DPRINT("NpfsListeningCancelRoutine() called for <%wZ>\n",
32 &Waiter
->Ccb
->Fcb
->PipeName
);
34 IoReleaseCancelSpinLock(Irp
->CancelIrql
);
37 KeLockMutex(&Waiter
->Ccb
->Fcb
->CcbListLock
);
38 RemoveEntryList(&Waiter
->Entry
);
39 KeUnlockMutex(&Waiter
->Ccb
->Fcb
->CcbListLock
);
41 Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
42 Irp
->IoStatus
.Information
= 0;
43 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
48 NpfsAddListeningServerInstance(PIRP Irp
,
51 PNPFS_WAITER_ENTRY Entry
;
54 Entry
= (PNPFS_WAITER_ENTRY
)&Irp
->Tail
.Overlay
.DriverContext
;
58 KeLockMutex(&Ccb
->Fcb
->CcbListLock
);
60 IoMarkIrpPending(Irp
);
61 InsertTailList(&Ccb
->Fcb
->WaiterListHead
, &Entry
->Entry
);
63 IoAcquireCancelSpinLock(&oldIrql
);
66 (void)IoSetCancelRoutine(Irp
, NpfsListeningCancelRoutine
);
67 IoReleaseCancelSpinLock(oldIrql
);
68 KeUnlockMutex(&Ccb
->Fcb
->CcbListLock
);
69 return STATUS_PENDING
;
71 IoReleaseCancelSpinLock(oldIrql
);
73 RemoveEntryList(&Entry
->Entry
);
75 Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
76 Irp
->IoStatus
.Information
= 0;
77 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
78 KeUnlockMutex(&Ccb
->Fcb
->CcbListLock
);
80 return STATUS_CANCELLED
;
85 NpfsConnectPipe(PIRP Irp
,
88 PIO_STACK_LOCATION IoStack
;
89 PFILE_OBJECT FileObject
;
91 PLIST_ENTRY current_entry
;
96 DPRINT("NpfsConnectPipe()\n");
98 if (Ccb
->PipeState
== FILE_PIPE_CONNECTED_STATE
)
100 KeResetEvent(&Ccb
->ConnectEvent
);
101 return STATUS_PIPE_CONNECTED
;
104 if (Ccb
->PipeState
== FILE_PIPE_CLOSING_STATE
)
105 return STATUS_PIPE_CLOSING
;
107 DPRINT("Waiting for connection...\n");
110 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
111 FileObject
= IoStack
->FileObject
;
112 Flags
= FileObject
->Flags
;
114 /* search for a listening client fcb */
115 KeLockMutex(&Fcb
->CcbListLock
);
117 current_entry
= Fcb
->ClientCcbListHead
.Flink
;
118 while (current_entry
!= &Fcb
->ClientCcbListHead
)
120 ClientCcb
= CONTAINING_RECORD(current_entry
,
124 if (ClientCcb
->PipeState
== 0)
126 /* found a passive (waiting) client CCB */
127 DPRINT("Passive (waiting) client CCB found -- wake the client\n");
128 KeSetEvent(&ClientCcb
->ConnectEvent
, IO_NO_INCREMENT
, FALSE
);
133 if (ClientCcb
->PipeState
== FILE_PIPE_LISTENING_STATE
)
135 /* found a listening client CCB */
136 DPRINT("Listening client CCB found -- connecting\n");
138 /* connect client and server CCBs */
139 Ccb
->OtherSide
= ClientCcb
;
140 ClientCcb
->OtherSide
= Ccb
;
142 /* set connected state */
143 Ccb
->PipeState
= FILE_PIPE_CONNECTED_STATE
;
144 ClientCcb
->PipeState
= FILE_PIPE_CONNECTED_STATE
;
146 KeUnlockMutex(&Fcb
->CcbListLock
);
148 /* FIXME: create and initialize data queues */
150 /* signal client's connect event */
151 DPRINT("Setting the ConnectEvent for %x\n", ClientCcb
);
152 KeSetEvent(&ClientCcb
->ConnectEvent
, IO_NO_INCREMENT
, FALSE
);
154 return STATUS_PIPE_CONNECTED
;
158 current_entry
= current_entry
->Flink
;
161 /* no listening client fcb found */
162 DPRINT("No listening client fcb found -- waiting for client\n");
164 Ccb
->PipeState
= FILE_PIPE_LISTENING_STATE
;
166 Status
= NpfsAddListeningServerInstance(Irp
, Ccb
);
168 KeUnlockMutex(&Fcb
->CcbListLock
);
170 if (Flags
& FO_SYNCHRONOUS_IO
)
172 KeWaitForSingleObject(&Ccb
->ConnectEvent
,
179 DPRINT("NpfsConnectPipe() done (Status %lx)\n", Status
);
186 NpfsDisconnectPipe(PNPFS_CCB Ccb
)
193 DPRINT("NpfsDisconnectPipe()\n");
196 KeLockMutex(&Fcb
->CcbListLock
);
198 if (Ccb
->PipeState
== FILE_PIPE_DISCONNECTED_STATE
)
200 DPRINT("Pipe is already disconnected\n");
201 Status
= STATUS_PIPE_DISCONNECTED
;
203 else if ((!Ccb
->OtherSide
) && (Ccb
->PipeState
== FILE_PIPE_CONNECTED_STATE
))
205 ExAcquireFastMutex(&Ccb
->DataListLock
);
206 Ccb
->PipeState
= FILE_PIPE_DISCONNECTED_STATE
;
207 ExReleaseFastMutex(&Ccb
->DataListLock
);
208 Status
= STATUS_SUCCESS
;
210 else if (Ccb
->PipeState
== FILE_PIPE_CONNECTED_STATE
)
212 Server
= (Ccb
->PipeEnd
== FILE_PIPE_SERVER_END
);
213 OtherSide
= Ccb
->OtherSide
;
214 //Ccb->OtherSide = NULL;
215 Ccb
->PipeState
= FILE_PIPE_DISCONNECTED_STATE
;
216 /* Lock the server first */
219 ExAcquireFastMutex(&Ccb
->DataListLock
);
220 ExAcquireFastMutex(&OtherSide
->DataListLock
);
224 ExAcquireFastMutex(&OtherSide
->DataListLock
);
225 ExAcquireFastMutex(&Ccb
->DataListLock
);
227 OtherSide
->PipeState
= FILE_PIPE_DISCONNECTED_STATE
;
228 //OtherSide->OtherSide = NULL;
230 * Signaling the write event. If is possible that an other
231 * thread waits for an empty buffer.
233 KeSetEvent(&OtherSide
->ReadEvent
, IO_NO_INCREMENT
, FALSE
);
234 KeSetEvent(&OtherSide
->WriteEvent
, IO_NO_INCREMENT
, FALSE
);
237 ExReleaseFastMutex(&OtherSide
->DataListLock
);
238 ExReleaseFastMutex(&Ccb
->DataListLock
);
242 ExReleaseFastMutex(&Ccb
->DataListLock
);
243 ExReleaseFastMutex(&OtherSide
->DataListLock
);
245 Status
= STATUS_SUCCESS
;
247 else if (Ccb
->PipeState
== FILE_PIPE_LISTENING_STATE
)
250 PNPFS_WAITER_ENTRY WaitEntry
= NULL
;
251 BOOLEAN Complete
= FALSE
;
254 Entry
= Ccb
->Fcb
->WaiterListHead
.Flink
;
255 while (Entry
!= &Ccb
->Fcb
->WaiterListHead
)
257 WaitEntry
= CONTAINING_RECORD(Entry
, NPFS_WAITER_ENTRY
, Entry
);
258 if (WaitEntry
->Ccb
== Ccb
)
260 RemoveEntryList(Entry
);
261 Irp
= CONTAINING_RECORD(Entry
, IRP
, Tail
.Overlay
.DriverContext
);
262 Complete
= (NULL
== IoSetCancelRoutine(Irp
, NULL
));
265 Entry
= Entry
->Flink
;
272 Irp
->IoStatus
.Status
= STATUS_PIPE_BROKEN
;
273 Irp
->IoStatus
.Information
= 0;
274 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
277 Ccb
->PipeState
= FILE_PIPE_DISCONNECTED_STATE
;
278 Status
= STATUS_SUCCESS
;
280 else if (Ccb
->PipeState
== FILE_PIPE_CLOSING_STATE
)
282 Status
= STATUS_PIPE_CLOSING
;
286 Status
= STATUS_UNSUCCESSFUL
;
288 KeUnlockMutex(&Fcb
->CcbListLock
);
294 NpfsWaitPipe(PIRP Irp
,
297 PLIST_ENTRY current_entry
;
300 PFILE_PIPE_WAIT_FOR_BUFFER WaitPipe
;
301 LARGE_INTEGER TimeOut
;
303 #ifdef USING_PROPER_NPFS_WAIT_SEMANTICS
305 UNICODE_STRING PipeName
;
308 DPRINT("NpfsWaitPipe\n");
310 WaitPipe
= (PFILE_PIPE_WAIT_FOR_BUFFER
)Irp
->AssociatedIrp
.SystemBuffer
;
312 #ifdef USING_PROPER_NPFS_WAIT_SEMANTICS
313 /* Fail, if the CCB does not represent the root directory */
314 if (Ccb
->Type
!= CCB_DIRECTORY
)
315 return STATUS_ILLEGAL_FUNCTION
;
317 /* Calculate the pipe name length and allocate the buffer */
318 PipeName
.Length
= WaitPipe
->NameLength
+ sizeof(WCHAR
);
319 PipeName
.MaximumLength
= PipeName
.Length
+ sizeof(WCHAR
);
320 PipeName
.Buffer
= ExAllocatePool(NonPagedPool
, PipeName
.MaximumLength
);
321 if (PipeName
.Buffer
== NULL
)
323 DPRINT1("Could not allocate memory for the pipe name!\n");
324 return STATUS_NO_MEMORY
;
327 /* Copy the pipe name into the buffer, prepend a backslash and append a 0 character */
328 PipeName
.Buffer
[0] = L
'\\';
329 RtlCopyMemory(&PipeName
.Buffer
[1],
331 WaitPipe
->NameLength
);
332 PipeName
.Buffer
[PipeName
.Length
/ sizeof(WCHAR
)] = 0;
334 DPRINT("Waiting for Pipe %wZ\n", &PipeName
);
339 /* Lock the pipe list */
340 KeLockMutex(&Vcb
->PipeListLock
);
342 /* File a pipe with the given name */
343 Fcb
= NpfsFindPipe(Vcb
,
346 /* Unlock the pipe list */
347 KeUnlockMutex(&Vcb
->PipeListLock
);
349 /* Release the pipe name buffer */
350 ExFreePool(PipeName
.Buffer
);
352 /* Fail if not pipe was found */
355 DPRINT("No pipe found!\n", Fcb
);
356 return STATUS_OBJECT_NAME_NOT_FOUND
;
359 DPRINT("Fcb %p\n", Fcb
);
363 if (Ccb
->PipeState
!= 0)
365 DPRINT("Pipe is not in passive (waiting) state!\n");
366 return STATUS_UNSUCCESSFUL
;
370 /* search for listening server */
371 current_entry
= Fcb
->ServerCcbListHead
.Flink
;
372 while (current_entry
!= &Fcb
->ServerCcbListHead
)
374 ServerCcb
= CONTAINING_RECORD(current_entry
,
378 if (ServerCcb
->PipeState
== FILE_PIPE_LISTENING_STATE
)
380 /* found a listening server CCB */
381 DPRINT("Listening server CCB found -- connecting\n");
383 return STATUS_SUCCESS
;
386 current_entry
= current_entry
->Flink
;
389 /* No listening server fcb found */
391 /* If no timeout specified, use the default one */
392 if (WaitPipe
->TimeoutSpecified
)
393 TimeOut
= WaitPipe
->Timeout
;
395 TimeOut
= Fcb
->TimeOut
;
398 Status
= KeWaitForSingleObject(&Ccb
->ConnectEvent
,
404 DPRINT("KeWaitForSingleObject() returned (Status %lx)\n", Status
);
411 * FUNCTION: Return current state of a pipe
413 * Irp = Pointer to I/O request packet
414 * IrpSp = Pointer to current stack location of Irp
416 * Status of operation
420 * FUNCTION: Peek at a pipe (get information about messages)
422 * Irp = Pointer to I/O request packet
423 * IoStack = Pointer to current stack location of Irp
425 * Status of operation
428 NpfsPeekPipe(PIRP Irp
,
429 PIO_STACK_LOCATION IoStack
)
431 ULONG OutputBufferLength
;
432 ULONG ReturnLength
= 0;
433 PFILE_PIPE_PEEK_BUFFER Reply
;
437 ULONG MessageCount
= 0;
439 ULONG ReadDataAvailable
;
442 DPRINT("NpfsPeekPipe\n");
444 OutputBufferLength
= IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
;
445 DPRINT("OutputBufferLength: %lu\n", OutputBufferLength
);
447 /* Validate parameters */
448 if (OutputBufferLength
< sizeof(FILE_PIPE_PEEK_BUFFER
))
450 DPRINT1("Buffer too small\n");
451 return STATUS_INVALID_PARAMETER
;
454 Ccb
= IoStack
->FileObject
->FsContext2
;
455 Reply
= (PFILE_PIPE_PEEK_BUFFER
)Irp
->AssociatedIrp
.SystemBuffer
;
459 Reply
->NamedPipeState
= Ccb
->PipeState
;
461 Reply
->ReadDataAvailable
= Ccb
->ReadDataAvailable
;
462 DPRINT("ReadDataAvailable: %lu\n", Ccb
->ReadDataAvailable
);
464 ExAcquireFastMutex(&Ccb
->DataListLock
);
465 BufferPtr
= Ccb
->ReadPtr
;
466 DPRINT("BufferPtr = %x\n", BufferPtr
);
467 if (Ccb
->Fcb
->PipeType
== FILE_PIPE_BYTE_STREAM_TYPE
)
469 DPRINT("Byte Stream Mode\n");
470 Reply
->MessageLength
= Ccb
->ReadDataAvailable
;
471 DPRINT("Reply->MessageLength %lu\n",Reply
->MessageLength
);
474 if (Reply
->Data
[0] && (OutputBufferLength
>= Ccb
->ReadDataAvailable
+ FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER
, Data
[0])))
476 ReturnLength
= Ccb
->ReadDataAvailable
;
477 memcpy(&Reply
->Data
[0], (PVOID
)BufferPtr
, Ccb
->ReadDataAvailable
);
482 DPRINT("Message Mode\n");
483 ReadDataAvailable
=Ccb
->ReadDataAvailable
;
485 if (ReadDataAvailable
> 0)
487 memcpy(&Reply
->MessageLength
, BufferPtr
, sizeof(ULONG
));
489 while ((ReadDataAvailable
> 0) && (BufferPtr
< Ccb
->WritePtr
))
491 memcpy(&MessageLength
, BufferPtr
, sizeof(MessageLength
));
493 ASSERT(MessageLength
> 0);
495 DPRINT("MessageLength = %lu\n",MessageLength
);
496 ReadDataAvailable
-= MessageLength
;
499 /* If its the first message, copy the Message if the size of buffer is large enough */
503 && (OutputBufferLength
>= (MessageLength
+ FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER
, Data
[0]))))
505 memcpy(&Reply
->Data
[0], (PVOID
)((ULONG_PTR
)BufferPtr
+ sizeof(MessageLength
)), MessageLength
);
506 ReturnLength
= MessageLength
;
510 BufferPtr
=(PVOID
)((ULONG_PTR
)BufferPtr
+ MessageLength
+ sizeof(MessageLength
));
511 DPRINT("BufferPtr = %x\n", BufferPtr
);
512 DPRINT("ReadDataAvailable: %lu\n", ReadDataAvailable
);
515 if (ReadDataAvailable
!= 0)
517 DPRINT1("Possible memory corruption.\n");
522 ExReleaseFastMutex(&Ccb
->DataListLock
);
524 Reply
->NumberOfMessages
= MessageCount
;
526 Irp
->IoStatus
.Information
= ReturnLength
+ FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER
, Data
[0]);
527 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
529 Status
= STATUS_SUCCESS
;
531 DPRINT("NpfsPeekPipe done\n");
538 NpfsFileSystemControl(PDEVICE_OBJECT DeviceObject
,
541 PIO_STACK_LOCATION IoStack
;
542 PFILE_OBJECT FileObject
;
548 DPRINT("NpfsFileSystemContol(DeviceObject %p Irp %p)\n", DeviceObject
, Irp
);
550 Vcb
= (PNPFS_VCB
)DeviceObject
->DeviceExtension
;
551 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
552 DPRINT("IoStack: %p\n", IoStack
);
553 FileObject
= IoStack
->FileObject
;
554 DPRINT("FileObject: %p\n", FileObject
);
555 Ccb
= FileObject
->FsContext2
;
556 DPRINT("CCB: %p\n", Ccb
);
558 DPRINT("Pipe: %p\n", Fcb
);
559 DPRINT("PipeName: %wZ\n", &Fcb
->PipeName
);
561 Irp
->IoStatus
.Information
= 0;
563 switch (IoStack
->Parameters
.FileSystemControl
.FsControlCode
)
565 case FSCTL_PIPE_ASSIGN_EVENT
:
566 DPRINT1("Assign event not implemented\n");
567 Status
= STATUS_NOT_IMPLEMENTED
;
570 case FSCTL_PIPE_DISCONNECT
:
571 DPRINT("Disconnecting pipe %wZ\n", &Fcb
->PipeName
);
572 Status
= NpfsDisconnectPipe(Ccb
);
575 case FSCTL_PIPE_LISTEN
:
576 DPRINT("Connecting pipe %wZ\n", &Fcb
->PipeName
);
577 Status
= NpfsConnectPipe(Irp
, Ccb
);
580 case FSCTL_PIPE_PEEK
:
581 DPRINT("Peeking pipe %wZ\n", &Fcb
->PipeName
);
582 Status
= NpfsPeekPipe(Irp
, (PIO_STACK_LOCATION
)IoStack
);
585 case FSCTL_PIPE_QUERY_EVENT
:
586 DPRINT1("Query event not implemented\n");
587 Status
= STATUS_NOT_IMPLEMENTED
;
590 case FSCTL_PIPE_TRANSCEIVE
:
591 /* If you implement this, please remove the workaround in
592 lib/kernel32/file/npipe.c function TransactNamedPipe() */
593 DPRINT1("Transceive not implemented\n");
594 Status
= STATUS_NOT_IMPLEMENTED
;
597 case FSCTL_PIPE_WAIT
:
598 DPRINT("Waiting for pipe %wZ\n", &Fcb
->PipeName
);
599 Status
= NpfsWaitPipe(Irp
, Ccb
);
602 case FSCTL_PIPE_IMPERSONATE
:
603 DPRINT1("Impersonate not implemented\n");
604 Status
= STATUS_NOT_IMPLEMENTED
;
607 case FSCTL_PIPE_SET_CLIENT_PROCESS
:
608 DPRINT1("Set client process not implemented\n");
609 Status
= STATUS_NOT_IMPLEMENTED
;
612 case FSCTL_PIPE_QUERY_CLIENT_PROCESS
:
613 DPRINT1("Query client process not implemented\n");
614 Status
= STATUS_NOT_IMPLEMENTED
;
617 case FSCTL_PIPE_INTERNAL_READ
:
618 DPRINT1("Internal read not implemented\n");
619 Status
= STATUS_NOT_IMPLEMENTED
;
622 case FSCTL_PIPE_INTERNAL_WRITE
:
623 DPRINT1("Internal write not implemented\n");
624 Status
= STATUS_NOT_IMPLEMENTED
;
627 case FSCTL_PIPE_INTERNAL_TRANSCEIVE
:
628 DPRINT1("Internal transceive not implemented\n");
629 Status
= STATUS_NOT_IMPLEMENTED
;
632 case FSCTL_PIPE_INTERNAL_READ_OVFLOW
:
633 DPRINT1("Internal read overflow not implemented\n");
634 Status
= STATUS_NOT_IMPLEMENTED
;
638 DPRINT1("Unrecognized IoControlCode: %x\n",
639 IoStack
->Parameters
.FileSystemControl
.FsControlCode
);
640 Status
= STATUS_UNSUCCESSFUL
;
643 if (Status
!= STATUS_PENDING
)
645 Irp
->IoStatus
.Status
= Status
;
647 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
655 NpfsFlushBuffers(PDEVICE_OBJECT DeviceObject
,
658 /* FIXME: Implement */
660 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
661 Irp
->IoStatus
.Information
= 0;
663 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
665 return STATUS_SUCCESS
;