[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / file / npipe.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32 Kernel Library
4 * FILE: lib/kernel32/file/npipe.c
5 * PURPOSE: Named Pipe Functions
6 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
7 * Ariadne ( ariadne@xs4all.nl)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include <k32.h>
13 #define NDEBUG
14 #include <debug.h>
15 DEBUG_CHANNEL(kernel32file);
16
17 //#define USING_PROPER_NPFS_WAIT_SEMANTICS
18
19 /* GLOBALS ********************************************************************/
20
21 LONG ProcessPipeId;
22
23 /* FUNCTIONS ******************************************************************/
24
25 /*
26 * @implemented
27 */
28 BOOL
29 WINAPI
30 CreatePipe(PHANDLE hReadPipe,
31 PHANDLE hWritePipe,
32 LPSECURITY_ATTRIBUTES lpPipeAttributes,
33 DWORD nSize)
34 {
35 WCHAR Buffer[64];
36 UNICODE_STRING PipeName;
37 OBJECT_ATTRIBUTES ObjectAttributes;
38 IO_STATUS_BLOCK StatusBlock;
39 LARGE_INTEGER DefaultTimeout;
40 NTSTATUS Status;
41 HANDLE ReadPipeHandle;
42 HANDLE WritePipeHandle;
43 LONG PipeId;
44 ULONG Attributes;
45 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
46
47 /* Set the timeout to 120 seconds */
48 DefaultTimeout.QuadPart = -1200000000;
49
50 /* Use default buffer size if desired */
51 if (!nSize) nSize = 0x1000;
52
53 /* Increase the Pipe ID */
54 PipeId = InterlockedIncrement(&ProcessPipeId);
55
56 /* Create the pipe name */
57 swprintf(Buffer,
58 L"\\Device\\NamedPipe\\Win32Pipes.%08x.%08x",
59 NtCurrentTeb()->ClientId.UniqueProcess,
60 PipeId);
61 RtlInitUnicodeString(&PipeName, Buffer);
62
63 /* Always use case insensitive */
64 Attributes = OBJ_CASE_INSENSITIVE;
65
66 /* Check if we got attributes */
67 if (lpPipeAttributes)
68 {
69 /* Use the attributes' SD instead */
70 SecurityDescriptor = lpPipeAttributes->lpSecurityDescriptor;
71
72 /* Set OBJ_INHERIT if requested */
73 if (lpPipeAttributes->bInheritHandle) Attributes |= OBJ_INHERIT;
74 }
75
76 /* Initialize the attributes */
77 InitializeObjectAttributes(&ObjectAttributes,
78 &PipeName,
79 Attributes,
80 NULL,
81 SecurityDescriptor);
82
83 /* Create the named pipe */
84 Status = NtCreateNamedPipeFile(&ReadPipeHandle,
85 GENERIC_READ |FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
86 &ObjectAttributes,
87 &StatusBlock,
88 FILE_SHARE_READ | FILE_SHARE_WRITE,
89 FILE_CREATE,
90 FILE_SYNCHRONOUS_IO_NONALERT,
91 FILE_PIPE_BYTE_STREAM_TYPE,
92 FILE_PIPE_BYTE_STREAM_MODE,
93 FILE_PIPE_QUEUE_OPERATION,
94 1,
95 nSize,
96 nSize,
97 &DefaultTimeout);
98 if (!NT_SUCCESS(Status))
99 {
100 /* Convert error and fail */
101 WARN("Status: %lx\n", Status);
102 BaseSetLastNTError(Status);
103 return FALSE;
104 }
105
106 /* Now try opening it for write access */
107 Status = NtOpenFile(&WritePipeHandle,
108 FILE_GENERIC_WRITE | SYNCHRONIZE,
109 &ObjectAttributes,
110 &StatusBlock,
111 FILE_SHARE_READ,
112 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
113 if (!NT_SUCCESS(Status))
114 {
115 /* Convert error and fail */
116 WARN("Status: %lx\n", Status);
117 NtClose(ReadPipeHandle);
118 BaseSetLastNTError(Status);
119 return FALSE;
120 }
121
122 /* Return both handles */
123 *hReadPipe = ReadPipeHandle;
124 *hWritePipe = WritePipeHandle;
125 return TRUE;
126 }
127
128 /*
129 * @implemented
130 */
131 HANDLE
132 WINAPI
133 CreateNamedPipeA(LPCSTR lpName,
134 DWORD dwOpenMode,
135 DWORD dwPipeMode,
136 DWORD nMaxInstances,
137 DWORD nOutBufferSize,
138 DWORD nInBufferSize,
139 DWORD nDefaultTimeOut,
140 LPSECURITY_ATTRIBUTES lpSecurityAttributes)
141 {
142 /* Call the W(ide) function */
143 ConvertWin32AnsiChangeApiToUnicodeApi(CreateNamedPipe,
144 lpName,
145 dwOpenMode,
146 dwPipeMode,
147 nMaxInstances,
148 nOutBufferSize,
149 nInBufferSize,
150 nDefaultTimeOut,
151 lpSecurityAttributes);
152 }
153
154
155 /*
156 * @implemented
157 */
158 HANDLE
159 WINAPI
160 CreateNamedPipeW(LPCWSTR lpName,
161 DWORD dwOpenMode,
162 DWORD dwPipeMode,
163 DWORD nMaxInstances,
164 DWORD nOutBufferSize,
165 DWORD nInBufferSize,
166 DWORD nDefaultTimeOut,
167 LPSECURITY_ATTRIBUTES lpSecurityAttributes)
168 {
169 UNICODE_STRING NamedPipeName;
170 BOOL Result;
171 NTSTATUS Status;
172 OBJECT_ATTRIBUTES ObjectAttributes;
173 HANDLE PipeHandle;
174 ACCESS_MASK DesiredAccess;
175 ULONG CreateOptions = 0;
176 ULONG WriteModeMessage;
177 ULONG ReadModeMessage;
178 ULONG NonBlocking;
179 IO_STATUS_BLOCK Iosb;
180 ULONG ShareAccess = 0, Attributes;
181 LARGE_INTEGER DefaultTimeOut;
182 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
183
184 /* Check for valid instances */
185 if (nMaxInstances == 0 || nMaxInstances > PIPE_UNLIMITED_INSTANCES)
186 {
187 /* Fail */
188 SetLastError(ERROR_INVALID_PARAMETER);
189 return INVALID_HANDLE_VALUE;
190 }
191
192 /* Convert to NT syntax */
193 if (nMaxInstances == PIPE_UNLIMITED_INSTANCES)
194 nMaxInstances = -1;
195
196 /* Convert the name */
197 Result = RtlDosPathNameToNtPathName_U(lpName,
198 &NamedPipeName,
199 NULL,
200 NULL);
201 if (!Result)
202 {
203 /* Conversion failed */
204 SetLastError(ERROR_PATH_NOT_FOUND);
205 return INVALID_HANDLE_VALUE;
206 }
207
208 TRACE("Pipe name: %wZ\n", &NamedPipeName);
209 TRACE("Pipe name: %S\n", NamedPipeName.Buffer);
210
211 /* Always case insensitive, check if we got extra attributes */
212 Attributes = OBJ_CASE_INSENSITIVE;
213 if(lpSecurityAttributes)
214 {
215 /* We did; get the security descriptor */
216 SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor;
217
218 /* And check if this is pipe's handle will beinheritable */
219 if (lpSecurityAttributes->bInheritHandle)
220 Attributes |= OBJ_INHERIT;
221 }
222
223 /* Now we can initialize the object attributes */
224 InitializeObjectAttributes(&ObjectAttributes,
225 &NamedPipeName,
226 Attributes,
227 NULL,
228 SecurityDescriptor);
229
230 /* Setup the default Desired Access */
231 DesiredAccess = SYNCHRONIZE | (dwOpenMode & (WRITE_DAC |
232 WRITE_OWNER |
233 ACCESS_SYSTEM_SECURITY));
234
235 /* Convert to NT Create Flags */
236 if (dwOpenMode & FILE_FLAG_WRITE_THROUGH)
237 {
238 CreateOptions |= FILE_WRITE_THROUGH;
239 }
240
241 if (!(dwOpenMode & FILE_FLAG_OVERLAPPED))
242 {
243 CreateOptions |= FILE_SYNCHRONOUS_IO_NONALERT;
244 }
245
246 /* Handle all open modes */
247 if (dwOpenMode & PIPE_ACCESS_OUTBOUND)
248 {
249 ShareAccess |= FILE_SHARE_READ;
250 DesiredAccess |= GENERIC_WRITE;
251 }
252
253 if (dwOpenMode & PIPE_ACCESS_INBOUND)
254 {
255 ShareAccess |= FILE_SHARE_WRITE;
256 DesiredAccess |= GENERIC_READ;
257 }
258
259 /* Handle the type flags */
260 if (dwPipeMode & PIPE_TYPE_MESSAGE)
261 {
262 WriteModeMessage = FILE_PIPE_MESSAGE_TYPE;
263 }
264 else
265 {
266 WriteModeMessage = FILE_PIPE_BYTE_STREAM_TYPE;
267 }
268
269 /* Handle the mode flags */
270 if (dwPipeMode & PIPE_READMODE_MESSAGE)
271 {
272 ReadModeMessage = FILE_PIPE_MESSAGE_MODE;
273 }
274 else
275 {
276 ReadModeMessage = FILE_PIPE_BYTE_STREAM_MODE;
277 }
278
279 /* Handle the blocking mode */
280 if (dwPipeMode & PIPE_NOWAIT)
281 {
282 NonBlocking = FILE_PIPE_COMPLETE_OPERATION;
283 }
284 else
285 {
286 NonBlocking = FILE_PIPE_QUEUE_OPERATION;
287 }
288
289 /* Check if we have a timeout */
290 if (nDefaultTimeOut)
291 {
292 /* Convert the time to NT format */
293 DefaultTimeOut.QuadPart = UInt32x32To64(nDefaultTimeOut, -10000);
294 }
295 else
296 {
297 /* Use default timeout of 50 ms */
298 DefaultTimeOut.QuadPart = -500000;
299 }
300
301 /* Now create the pipe */
302 Status = NtCreateNamedPipeFile(&PipeHandle,
303 DesiredAccess,
304 &ObjectAttributes,
305 &Iosb,
306 ShareAccess,
307 FILE_OPEN_IF,
308 CreateOptions,
309 WriteModeMessage,
310 ReadModeMessage,
311 NonBlocking,
312 nMaxInstances,
313 nInBufferSize,
314 nOutBufferSize,
315 &DefaultTimeOut);
316
317 /* Normalize special error codes */
318 if ((Status == STATUS_INVALID_DEVICE_REQUEST) ||
319 (Status == STATUS_NOT_SUPPORTED))
320 {
321 Status = STATUS_OBJECT_NAME_INVALID;
322 }
323
324 /* Free the name */
325 RtlFreeHeap(RtlGetProcessHeap(),
326 0,
327 NamedPipeName.Buffer);
328
329 /* Check status */
330 if (!NT_SUCCESS(Status))
331 {
332 /* Failed to create it */
333 WARN("NtCreateNamedPipe failed (Status %x)!\n", Status);
334 BaseSetLastNTError (Status);
335 return INVALID_HANDLE_VALUE;
336 }
337
338 /* Return the handle */
339 return PipeHandle;
340 }
341
342
343 /*
344 * @implemented
345 */
346 BOOL
347 WINAPI
348 WaitNamedPipeA(LPCSTR lpNamedPipeName,
349 DWORD nTimeOut)
350 {
351 BOOL r;
352 UNICODE_STRING NameU;
353
354 /* Convert the name to Unicode */
355 Basep8BitStringToDynamicUnicodeString(&NameU, lpNamedPipeName);
356
357 /* Call the Unicode API */
358 r = WaitNamedPipeW(NameU.Buffer, nTimeOut);
359
360 /* Free the Unicode string */
361 RtlFreeUnicodeString(&NameU);
362
363 /* Return result */
364 return r;
365 }
366
367
368 /*
369 * When NPFS will work properly, use this code instead. It is compatible with
370 * Microsoft's NPFS.SYS. The main difference is that:
371 * - This code actually respects the timeout instead of ignoring it!
372 * - This code validates and creates the proper names for both UNC and local pipes
373 * - On NT, you open the *root* pipe directory (either \DosDevices\Pipe or
374 * \DosDevices\Unc\Server\Pipe) and then send the pipe to wait on in the
375 * FILE_PIPE_WAIT_FOR_BUFFER structure.
376 */
377 #ifdef USING_PROPER_NPFS_WAIT_SEMANTICS
378 /*
379 * @implemented
380 */
381 BOOL
382 WINAPI
383 WaitNamedPipeW(LPCWSTR lpNamedPipeName,
384 DWORD nTimeOut)
385 {
386 UNICODE_STRING NamedPipeName, NewName, DevicePath, PipePrefix;
387 ULONG NameLength;
388 ULONG i;
389 PWCHAR p;
390 ULONG Type;
391 OBJECT_ATTRIBUTES ObjectAttributes;
392 NTSTATUS Status;
393 HANDLE FileHandle;
394 IO_STATUS_BLOCK IoStatusBlock;
395 ULONG WaitPipeInfoSize;
396 PFILE_PIPE_WAIT_FOR_BUFFER WaitPipeInfo;
397
398 /* Start by making a unicode string of the name */
399 TRACE("Sent path: %S\n", lpNamedPipeName);
400 RtlCreateUnicodeString(&NamedPipeName, lpNamedPipeName);
401 NameLength = NamedPipeName.Length / sizeof(WCHAR);
402
403 /* All slashes must become backslashes */
404 for (i = 0; i < NameLength; i++)
405 {
406 /* Check and convert */
407 if (NamedPipeName.Buffer[i] == L'/') NamedPipeName.Buffer[i] = L'\\';
408 }
409
410 /* Find the path type of the name we were given */
411 NewName = NamedPipeName;
412 Type = RtlDetermineDosPathNameType_U(lpNamedPipeName);
413
414 /* Check if this was a device path, ie : "\\.\pipe\name" */
415 if (Type == RtlPathTypeLocalDevice)
416 {
417 /* Make sure it's a valid prefix */
418 RtlInitUnicodeString(&PipePrefix, L"\\\\.\\pipe\\");
419 RtlPrefixString((PANSI_STRING)&PipePrefix, (PANSI_STRING)&NewName, TRUE);
420
421 /* Move past it */
422 NewName.Buffer += 9;
423 NewName.Length -= 9 * sizeof(WCHAR);
424
425 /* Initialize the Dos Devices name */
426 TRACE("NewName: %wZ\n", &NewName);
427 RtlInitUnicodeString(&DevicePath, L"\\DosDevices\\pipe\\");
428 }
429 else if (Type == RtlPathTypeRootLocalDevice)
430 {
431 /* The path is \\server\\pipe\name; find the pipename itself */
432 p = &NewName.Buffer[2];
433
434 /* First loop to get past the server name */
435 do
436 {
437 /* Check if this is a backslash */
438 if (*p == L'\\') break;
439
440 /* Check next */
441 p++;
442 } while (*p);
443
444 /* Now make sure the full name contains "pipe\" */
445 if ((*p) && !(_wcsnicmp(p + 1, L"pipe\\", sizeof("pipe\\"))))
446 {
447 /* Get to the pipe name itself now */
448 p += sizeof("pipe\\") - 1;
449 }
450 else
451 {
452 /* The name is invalid */
453 WARN("Invalid name!\n");
454 BaseSetLastNTError(STATUS_OBJECT_PATH_SYNTAX_BAD);
455 return FALSE;
456 }
457
458 /* FIXME: Open \DosDevices\Unc\Server\Pipe\Name */
459 }
460 else
461 {
462 WARN("Invalid path type\n");
463 BaseSetLastNTError(STATUS_OBJECT_PATH_SYNTAX_BAD);
464 return FALSE;
465 }
466
467 /* Now calculate the total length of the structure and allocate it */
468 WaitPipeInfoSize = FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) +
469 NewName.Length;
470 WaitPipeInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, WaitPipeInfoSize);
471 if (WaitPipeInfo == NULL)
472 {
473 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
474 return FALSE;
475 }
476
477 /* Initialize the object attributes */
478 TRACE("Opening: %wZ\n", &DevicePath);
479 InitializeObjectAttributes(&ObjectAttributes,
480 &DevicePath,
481 OBJ_CASE_INSENSITIVE,
482 NULL,
483 NULL);
484
485 /* Open the path */
486 Status = NtOpenFile(&FileHandle,
487 FILE_READ_ATTRIBUTES | SYNCHRONIZE,
488 &ObjectAttributes,
489 &IoStatusBlock,
490 FILE_SHARE_READ | FILE_SHARE_WRITE,
491 FILE_SYNCHRONOUS_IO_NONALERT);
492 if (!NT_SUCCESS(Status))
493 {
494 /* Fail; couldn't open */
495 WARN("Status: %lx\n", Status);
496 BaseSetLastNTError(Status);
497 RtlFreeUnicodeString(&NamedPipeName);
498 RtlFreeHeap(RtlGetProcessHeap(), 0, WaitPipeInfo);
499 return FALSE;
500 }
501
502 /* Check what timeout we got */
503 if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT)
504 {
505 /* Don't use a timeout */
506 WaitPipeInfo->TimeoutSpecified = FALSE;
507 }
508 else
509 {
510 /* Check if we should wait forever */
511 if (nTimeOut == NMPWAIT_WAIT_FOREVER)
512 {
513 /* Set the max */
514 WaitPipeInfo->Timeout.LowPart = 0;
515 WaitPipeInfo->Timeout.HighPart = 0x80000000;
516 }
517 else
518 {
519 /* Convert to NT format */
520 WaitPipeInfo->Timeout.QuadPart = UInt32x32To64(-10000, nTimeOut);
521 }
522
523 /* In both cases, we do have a timeout */
524 WaitPipeInfo->TimeoutSpecified = TRUE;
525 }
526
527 /* Set the length and copy the name */
528 WaitPipeInfo->NameLength = NewName.Length;
529 RtlCopyMemory(WaitPipeInfo->Name, NewName.Buffer, NewName.Length);
530
531 /* Get rid of the full name */
532 RtlFreeUnicodeString(&NamedPipeName);
533
534 /* Let NPFS know of our request */
535 Status = NtFsControlFile(FileHandle,
536 NULL,
537 NULL,
538 NULL,
539 &IoStatusBlock,
540 FSCTL_PIPE_WAIT,
541 WaitPipeInfo,
542 WaitPipeInfoSize,
543 NULL,
544 0);
545
546 /* Free our pipe info data and close the handle */
547 RtlFreeHeap(RtlGetProcessHeap(), 0, WaitPipeInfo);
548 NtClose(FileHandle);
549
550 /* Check the status */
551 if (!NT_SUCCESS(Status))
552 {
553 /* Failure to wait on the pipe */
554 WARN("Status: %lx\n", Status);
555 BaseSetLastNTError (Status);
556 return FALSE;
557 }
558
559 /* Success */
560 return TRUE;
561 }
562 #else
563 /*
564 * @implemented
565 */
566 BOOL
567 WINAPI
568 WaitNamedPipeW(LPCWSTR lpNamedPipeName,
569 DWORD nTimeOut)
570 {
571 UNICODE_STRING NamedPipeName;
572 NTSTATUS Status;
573 OBJECT_ATTRIBUTES ObjectAttributes;
574 FILE_PIPE_WAIT_FOR_BUFFER WaitPipe;
575 HANDLE FileHandle;
576 IO_STATUS_BLOCK Iosb;
577
578 if (RtlDosPathNameToNtPathName_U(lpNamedPipeName,
579 &NamedPipeName,
580 NULL,
581 NULL) == FALSE)
582 {
583 return FALSE;
584 }
585
586 InitializeObjectAttributes(&ObjectAttributes,
587 &NamedPipeName,
588 OBJ_CASE_INSENSITIVE,
589 NULL,
590 NULL);
591 Status = NtOpenFile(&FileHandle,
592 FILE_READ_ATTRIBUTES | SYNCHRONIZE,
593 &ObjectAttributes,
594 &Iosb,
595 FILE_SHARE_READ | FILE_SHARE_WRITE,
596 FILE_SYNCHRONOUS_IO_NONALERT);
597 if (!NT_SUCCESS(Status))
598 {
599 BaseSetLastNTError(Status);
600 RtlFreeUnicodeString(&NamedPipeName);
601 return FALSE;
602 }
603
604 /* Check what timeout we got */
605 if (nTimeOut == NMPWAIT_WAIT_FOREVER)
606 {
607 /* Don't use a timeout */
608 WaitPipe.TimeoutSpecified = FALSE;
609 }
610 else
611 {
612 /* Check if default */
613 if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT)
614 {
615 /* Set it to 0 */
616 WaitPipe.Timeout.LowPart = 0;
617 WaitPipe.Timeout.HighPart = 0;
618 }
619 else
620 {
621 /* Convert to NT format */
622 WaitPipe.Timeout.QuadPart = UInt32x32To64(-10000, nTimeOut);
623 }
624
625 /* In both cases, we do have a timeout */
626 WaitPipe.TimeoutSpecified = TRUE;
627 }
628
629 Status = NtFsControlFile(FileHandle,
630 NULL,
631 NULL,
632 NULL,
633 &Iosb,
634 FSCTL_PIPE_WAIT,
635 &WaitPipe,
636 sizeof(WaitPipe),
637 NULL,
638 0);
639 NtClose(FileHandle);
640 if (!NT_SUCCESS(Status))
641 {
642 BaseSetLastNTError(Status);
643 RtlFreeUnicodeString(&NamedPipeName);
644 return FALSE;
645 }
646
647 RtlFreeUnicodeString(&NamedPipeName);
648 return TRUE;
649 }
650 #endif
651
652
653 /*
654 * @implemented
655 */
656 BOOL
657 WINAPI
658 ConnectNamedPipe(IN HANDLE hNamedPipe,
659 IN LPOVERLAPPED lpOverlapped)
660 {
661 NTSTATUS Status;
662
663 if (lpOverlapped != NULL)
664 {
665 PVOID ApcContext;
666
667 lpOverlapped->Internal = STATUS_PENDING;
668 ApcContext = (((ULONG_PTR)lpOverlapped->hEvent & 0x1) ? NULL : lpOverlapped);
669
670 Status = NtFsControlFile(hNamedPipe,
671 lpOverlapped->hEvent,
672 NULL,
673 ApcContext,
674 (PIO_STATUS_BLOCK)lpOverlapped,
675 FSCTL_PIPE_LISTEN,
676 NULL,
677 0,
678 NULL,
679 0);
680
681 /* return FALSE in case of failure and pending operations! */
682 if (!NT_SUCCESS(Status) || Status == STATUS_PENDING)
683 {
684 BaseSetLastNTError(Status);
685 return FALSE;
686 }
687 }
688 else
689 {
690 IO_STATUS_BLOCK Iosb;
691
692 Status = NtFsControlFile(hNamedPipe,
693 NULL,
694 NULL,
695 NULL,
696 &Iosb,
697 FSCTL_PIPE_LISTEN,
698 NULL,
699 0,
700 NULL,
701 0);
702
703 /* wait in case operation is pending */
704 if (Status == STATUS_PENDING)
705 {
706 Status = NtWaitForSingleObject(hNamedPipe,
707 FALSE,
708 NULL);
709 if (NT_SUCCESS(Status))
710 {
711 Status = Iosb.Status;
712 }
713 }
714
715 if (!NT_SUCCESS(Status))
716 {
717 BaseSetLastNTError(Status);
718 return FALSE;
719 }
720 }
721
722 return TRUE;
723 }
724
725
726 /*
727 * @implemented
728 */
729 BOOL
730 WINAPI
731 SetNamedPipeHandleState(HANDLE hNamedPipe,
732 LPDWORD lpMode,
733 LPDWORD lpMaxCollectionCount,
734 LPDWORD lpCollectDataTimeout)
735 {
736 IO_STATUS_BLOCK Iosb;
737 NTSTATUS Status;
738
739 /* Check if the Mode is being changed */
740 if (lpMode)
741 {
742 FILE_PIPE_INFORMATION Settings;
743
744 /* Set the Completion Mode */
745 Settings.CompletionMode = (*lpMode & PIPE_NOWAIT) ?
746 FILE_PIPE_COMPLETE_OPERATION : FILE_PIPE_QUEUE_OPERATION;
747
748 /* Set the Read Mode */
749 Settings.ReadMode = (*lpMode & PIPE_READMODE_MESSAGE) ?
750 FILE_PIPE_MESSAGE_MODE: FILE_PIPE_BYTE_STREAM_MODE;
751
752 /* Send the changes to the Driver */
753 Status = NtSetInformationFile(hNamedPipe,
754 &Iosb,
755 &Settings,
756 sizeof(FILE_PIPE_INFORMATION),
757 FilePipeInformation);
758 if (!NT_SUCCESS(Status))
759 {
760 BaseSetLastNTError(Status);
761 return FALSE;
762 }
763 }
764
765 /* Check if the Collection count or Timeout are being changed */
766 if (lpMaxCollectionCount || lpCollectDataTimeout)
767 {
768 FILE_PIPE_REMOTE_INFORMATION RemoteSettings;
769
770 /* Setting one without the other would delete it, so we read old one */
771 if (!lpMaxCollectionCount || !lpCollectDataTimeout)
772 {
773 Status = NtQueryInformationFile(hNamedPipe,
774 &Iosb,
775 &RemoteSettings,
776 sizeof(FILE_PIPE_REMOTE_INFORMATION),
777 FilePipeRemoteInformation);
778 if (!NT_SUCCESS(Status))
779 {
780 BaseSetLastNTError(Status);
781 return FALSE;
782 }
783 }
784
785 /* Now set the new settings */
786 RemoteSettings.MaximumCollectionCount = (lpMaxCollectionCount) ?
787 *lpMaxCollectionCount :
788 RemoteSettings.MaximumCollectionCount;
789 if (lpCollectDataTimeout)
790 {
791 /* Convert it to Quad */
792 RemoteSettings.CollectDataTime.QuadPart =
793 -(LONGLONG)UInt32x32To64(10000, *lpCollectDataTimeout);
794 }
795
796 /* Tell the driver to change them */
797 Status = NtSetInformationFile(hNamedPipe,
798 &Iosb,
799 &RemoteSettings,
800 sizeof(FILE_PIPE_REMOTE_INFORMATION),
801 FilePipeRemoteInformation);
802 if (!NT_SUCCESS(Status))
803 {
804 BaseSetLastNTError(Status);
805 return FALSE;
806 }
807 }
808
809 return TRUE;
810 }
811
812
813 /*
814 * @implemented
815 */
816 BOOL
817 WINAPI
818 CallNamedPipeA(LPCSTR lpNamedPipeName,
819 LPVOID lpInBuffer,
820 DWORD nInBufferSize,
821 LPVOID lpOutBuffer,
822 DWORD nOutBufferSize,
823 LPDWORD lpBytesRead,
824 DWORD nTimeOut)
825 {
826 PUNICODE_STRING PipeName = &NtCurrentTeb()->StaticUnicodeString;
827 ANSI_STRING AnsiPipe;
828
829 /* Initialize the string as ANSI_STRING and convert to Unicode */
830 RtlInitAnsiString(&AnsiPipe, (LPSTR)lpNamedPipeName);
831 RtlAnsiStringToUnicodeString(PipeName, &AnsiPipe, FALSE);
832
833 /* Call the Unicode function */
834 return CallNamedPipeW(PipeName->Buffer,
835 lpInBuffer,
836 nInBufferSize,
837 lpOutBuffer,
838 nOutBufferSize,
839 lpBytesRead,
840 nTimeOut);
841 }
842
843
844 /*
845 * @implemented
846 */
847 BOOL
848 WINAPI
849 CallNamedPipeW(LPCWSTR lpNamedPipeName,
850 LPVOID lpInBuffer,
851 DWORD nInBufferSize,
852 LPVOID lpOutBuffer,
853 DWORD nOutBufferSize,
854 LPDWORD lpBytesRead,
855 DWORD nTimeOut)
856 {
857 HANDLE hPipe;
858 BOOL bRetry = TRUE;
859 BOOL bError;
860 DWORD dwPipeMode;
861
862 while (TRUE)
863 {
864 /* Try creating it */
865 hPipe = CreateFileW(lpNamedPipeName,
866 GENERIC_READ | GENERIC_WRITE,
867 FILE_SHARE_READ | FILE_SHARE_WRITE,
868 NULL,
869 OPEN_EXISTING,
870 FILE_ATTRIBUTE_NORMAL,
871 NULL);
872
873 /* Success, break out */
874 if (hPipe != INVALID_HANDLE_VALUE)
875 break;
876
877 /* Already tried twice, give up */
878 if (bRetry == FALSE)
879 return FALSE;
880
881 /* Wait on it */
882 WaitNamedPipeW(lpNamedPipeName, nTimeOut);
883
884 /* Get ready to try again */
885 bRetry = FALSE;
886 }
887
888 /* Set the pipe mode */
889 dwPipeMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
890 bError = SetNamedPipeHandleState(hPipe, &dwPipeMode, NULL, NULL);
891 if (!bError)
892 {
893 /* Couldn't change state, fail */
894 CloseHandle(hPipe);
895 return FALSE;
896 }
897
898 /* Do the transact */
899 bError = TransactNamedPipe(hPipe,
900 lpInBuffer,
901 nInBufferSize,
902 lpOutBuffer,
903 nOutBufferSize,
904 lpBytesRead,
905 NULL);
906
907 /* Close the handle */
908 CloseHandle(hPipe);
909
910 return bError;
911 }
912
913
914 /*
915 * @implemented
916 */
917 BOOL
918 WINAPI
919 DisconnectNamedPipe(HANDLE hNamedPipe)
920 {
921 IO_STATUS_BLOCK Iosb;
922 NTSTATUS Status;
923
924 /* Send the FSCTL to the driver */
925 Status = NtFsControlFile(hNamedPipe,
926 NULL,
927 NULL,
928 NULL,
929 &Iosb,
930 FSCTL_PIPE_DISCONNECT,
931 NULL,
932 0,
933 NULL,
934 0);
935 if (Status == STATUS_PENDING)
936 {
937 /* Wait on NPFS to finish and get updated status */
938 Status = NtWaitForSingleObject(hNamedPipe, FALSE, NULL);
939 if (NT_SUCCESS(Status))
940 Status = Iosb.Status;
941 }
942
943 /* Check for error */
944 if (!NT_SUCCESS(Status))
945 {
946 /* Fail */
947 BaseSetLastNTError(Status);
948 return FALSE;
949 }
950
951 return TRUE;
952 }
953
954
955 /*
956 * @unimplemented
957 */
958 BOOL
959 WINAPI
960 GetNamedPipeHandleStateW(HANDLE hNamedPipe,
961 LPDWORD lpState,
962 LPDWORD lpCurInstances,
963 LPDWORD lpMaxCollectionCount,
964 LPDWORD lpCollectDataTimeout,
965 LPWSTR lpUserName,
966 DWORD nMaxUserNameSize)
967 {
968 IO_STATUS_BLOCK StatusBlock;
969 NTSTATUS Status;
970
971 if (lpState != NULL)
972 {
973 FILE_PIPE_INFORMATION PipeInfo;
974
975 Status = NtQueryInformationFile(hNamedPipe,
976 &StatusBlock,
977 &PipeInfo,
978 sizeof(FILE_PIPE_INFORMATION),
979 FilePipeInformation);
980 if (!NT_SUCCESS(Status))
981 {
982 BaseSetLastNTError(Status);
983 return FALSE;
984 }
985
986 *lpState = ((PipeInfo.CompletionMode != FILE_PIPE_QUEUE_OPERATION) ? PIPE_NOWAIT : PIPE_WAIT);
987 *lpState |= ((PipeInfo.ReadMode != FILE_PIPE_BYTE_STREAM_MODE) ? PIPE_READMODE_MESSAGE : PIPE_READMODE_BYTE);
988 }
989
990 if(lpCurInstances != NULL)
991 {
992 FILE_PIPE_LOCAL_INFORMATION LocalInfo;
993
994 Status = NtQueryInformationFile(hNamedPipe,
995 &StatusBlock,
996 &LocalInfo,
997 sizeof(FILE_PIPE_LOCAL_INFORMATION),
998 FilePipeLocalInformation);
999 if (!NT_SUCCESS(Status))
1000 {
1001 BaseSetLastNTError(Status);
1002 return FALSE;
1003 }
1004
1005 *lpCurInstances = min(LocalInfo.CurrentInstances, PIPE_UNLIMITED_INSTANCES);
1006 }
1007
1008 if (lpMaxCollectionCount != NULL || lpCollectDataTimeout != NULL)
1009 {
1010 FILE_PIPE_REMOTE_INFORMATION RemoteInfo;
1011
1012 Status = NtQueryInformationFile(hNamedPipe,
1013 &StatusBlock,
1014 &RemoteInfo,
1015 sizeof(FILE_PIPE_REMOTE_INFORMATION),
1016 FilePipeRemoteInformation);
1017 if (!NT_SUCCESS(Status))
1018 {
1019 BaseSetLastNTError(Status);
1020 return FALSE;
1021 }
1022
1023 if (lpMaxCollectionCount != NULL)
1024 {
1025 *lpMaxCollectionCount = RemoteInfo.MaximumCollectionCount;
1026 }
1027
1028 if(lpCollectDataTimeout != NULL)
1029 {
1030 /* FIXME */
1031 *lpCollectDataTimeout = 0;
1032 }
1033 }
1034
1035 if (lpUserName != NULL)
1036 {
1037 /* FIXME - open the thread token, call ImpersonateNamedPipeClient() and
1038 retreive the user name with GetUserName(), revert the impersonation
1039 and finally restore the thread token */
1040 }
1041
1042 return TRUE;
1043 }
1044
1045
1046 /*
1047 * @implemented
1048 */
1049 BOOL
1050 WINAPI
1051 GetNamedPipeHandleStateA(HANDLE hNamedPipe,
1052 LPDWORD lpState,
1053 LPDWORD lpCurInstances,
1054 LPDWORD lpMaxCollectionCount,
1055 LPDWORD lpCollectDataTimeout,
1056 LPSTR lpUserName,
1057 DWORD nMaxUserNameSize)
1058 {
1059 UNICODE_STRING UserNameW = { 0, 0, NULL };
1060 ANSI_STRING UserNameA;
1061 BOOL Ret;
1062
1063 if(lpUserName != NULL)
1064 {
1065 UserNameW.MaximumLength = (USHORT)nMaxUserNameSize * sizeof(WCHAR);
1066 UserNameW.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, UserNameW.MaximumLength);
1067 if (UserNameW.Buffer == NULL)
1068 {
1069 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1070 return FALSE;
1071 }
1072
1073 UserNameA.Buffer = lpUserName;
1074 UserNameA.Length = 0;
1075 UserNameA.MaximumLength = (USHORT)nMaxUserNameSize;
1076 }
1077
1078 Ret = GetNamedPipeHandleStateW(hNamedPipe,
1079 lpState,
1080 lpCurInstances,
1081 lpMaxCollectionCount,
1082 lpCollectDataTimeout,
1083 UserNameW.Buffer,
1084 nMaxUserNameSize);
1085 if (Ret && lpUserName != NULL)
1086 {
1087 NTSTATUS Status;
1088
1089 RtlInitUnicodeString(&UserNameW, UserNameW.Buffer);
1090 Status = RtlUnicodeStringToAnsiString(&UserNameA, &UserNameW, FALSE);
1091 if (!NT_SUCCESS(Status))
1092 {
1093 BaseSetLastNTError(Status);
1094 Ret = FALSE;
1095 }
1096 }
1097
1098 if (UserNameW.Buffer != NULL)
1099 {
1100 RtlFreeHeap(RtlGetProcessHeap(), 0, UserNameW.Buffer);
1101 }
1102
1103 return Ret;
1104 }
1105
1106
1107 /*
1108 * @implemented
1109 */
1110 BOOL
1111 WINAPI
1112 GetNamedPipeInfo(HANDLE hNamedPipe,
1113 LPDWORD lpFlags,
1114 LPDWORD lpOutBufferSize,
1115 LPDWORD lpInBufferSize,
1116 LPDWORD lpMaxInstances)
1117 {
1118 FILE_PIPE_LOCAL_INFORMATION PipeLocalInformation;
1119 IO_STATUS_BLOCK StatusBlock;
1120 NTSTATUS Status;
1121
1122 Status = NtQueryInformationFile(hNamedPipe,
1123 &StatusBlock,
1124 &PipeLocalInformation,
1125 sizeof(FILE_PIPE_LOCAL_INFORMATION),
1126 FilePipeLocalInformation);
1127 if (!NT_SUCCESS(Status))
1128 {
1129 BaseSetLastNTError(Status);
1130 return FALSE;
1131 }
1132
1133 if (lpFlags != NULL)
1134 {
1135 *lpFlags = (PipeLocalInformation.NamedPipeEnd == FILE_PIPE_SERVER_END) ? PIPE_SERVER_END : PIPE_CLIENT_END;
1136 *lpFlags |= (PipeLocalInformation.NamedPipeType == 1) ? PIPE_TYPE_MESSAGE : PIPE_TYPE_BYTE;
1137 }
1138
1139 if (lpOutBufferSize != NULL)
1140 *lpOutBufferSize = PipeLocalInformation.OutboundQuota;
1141
1142 if (lpInBufferSize != NULL)
1143 *lpInBufferSize = PipeLocalInformation.InboundQuota;
1144
1145 if (lpMaxInstances != NULL)
1146 {
1147 if (PipeLocalInformation.MaximumInstances >= 255)
1148 *lpMaxInstances = PIPE_UNLIMITED_INSTANCES;
1149 else
1150 *lpMaxInstances = PipeLocalInformation.MaximumInstances;
1151 }
1152
1153 return TRUE;
1154 }
1155
1156
1157 /*
1158 * @implemented
1159 */
1160 BOOL
1161 WINAPI
1162 PeekNamedPipe(HANDLE hNamedPipe,
1163 LPVOID lpBuffer,
1164 DWORD nBufferSize,
1165 LPDWORD lpBytesRead,
1166 LPDWORD lpTotalBytesAvail,
1167 LPDWORD lpBytesLeftThisMessage)
1168 {
1169 PFILE_PIPE_PEEK_BUFFER Buffer;
1170 IO_STATUS_BLOCK Iosb;
1171 ULONG BufferSize;
1172 ULONG BytesRead;
1173 NTSTATUS Status;
1174
1175 /* Calculate the buffer space that we'll need and allocate it */
1176 BufferSize = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[nBufferSize]);
1177 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize);
1178 if (Buffer == NULL)
1179 {
1180 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1181 return FALSE;
1182 }
1183
1184 /* Tell the driver to seek */
1185 Status = NtFsControlFile(hNamedPipe,
1186 NULL,
1187 NULL,
1188 NULL,
1189 &Iosb,
1190 FSCTL_PIPE_PEEK,
1191 NULL,
1192 0,
1193 Buffer,
1194 BufferSize);
1195 if (Status == STATUS_PENDING)
1196 {
1197 /* Wait for npfs to be done, and update the status */
1198 Status = NtWaitForSingleObject(hNamedPipe, FALSE, NULL);
1199 if (NT_SUCCESS(Status))
1200 Status = Iosb.Status;
1201 }
1202
1203 /* Overflow is success for us */
1204 if (Status == STATUS_BUFFER_OVERFLOW)
1205 Status = STATUS_SUCCESS;
1206
1207 /* If we failed */
1208 if (!NT_SUCCESS(Status))
1209 {
1210 /* Free the buffer and return failure */
1211 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1212 BaseSetLastNTError(Status);
1213 return FALSE;
1214 }
1215
1216 /* Check if caller requested bytes available */
1217 if (lpTotalBytesAvail)
1218 {
1219 /* Return bytes available */
1220 *lpTotalBytesAvail = Buffer->ReadDataAvailable;
1221 }
1222
1223 /* Calculate the bytes returned, minus our structure overhead */
1224 BytesRead = (ULONG)(Iosb.Information -
1225 FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]));
1226 ASSERT(BytesRead <= nBufferSize);
1227
1228 /* Check if caller requested bytes read */
1229 if (lpBytesRead)
1230 {
1231 /* Return the bytes read */
1232 *lpBytesRead = BytesRead;
1233 }
1234
1235 /* Check if caller requested bytes left */
1236 if (lpBytesLeftThisMessage)
1237 {
1238 /* Calculate total minus what we returned and our structure overhead */
1239 *lpBytesLeftThisMessage = Buffer->MessageLength - BytesRead;
1240 }
1241
1242 /* Check if the caller wanted to see the actual data */
1243 if (lpBuffer)
1244 {
1245 /* Give him what he wants */
1246 RtlCopyMemory(lpBuffer,
1247 Buffer->Data,
1248 BytesRead);
1249 }
1250
1251 /* Free the buffer */
1252 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1253
1254 return TRUE;
1255 }
1256
1257
1258 /*
1259 * @implemented
1260 */
1261 BOOL
1262 WINAPI
1263 TransactNamedPipe(IN HANDLE hNamedPipe,
1264 IN LPVOID lpInBuffer,
1265 IN DWORD nInBufferSize,
1266 OUT LPVOID lpOutBuffer,
1267 IN DWORD nOutBufferSize,
1268 OUT LPDWORD lpBytesRead OPTIONAL,
1269 IN LPOVERLAPPED lpOverlapped OPTIONAL)
1270 {
1271 NTSTATUS Status;
1272
1273 if (lpBytesRead != NULL)
1274 {
1275 *lpBytesRead = 0;
1276 }
1277
1278 if (lpOverlapped != NULL)
1279 {
1280 PVOID ApcContext;
1281
1282 ApcContext = (((ULONG_PTR)lpOverlapped->hEvent & 0x1) ? NULL : lpOverlapped);
1283 lpOverlapped->Internal = STATUS_PENDING;
1284
1285 Status = NtFsControlFile(hNamedPipe,
1286 lpOverlapped->hEvent,
1287 NULL,
1288 ApcContext,
1289 (PIO_STATUS_BLOCK)lpOverlapped,
1290 FSCTL_PIPE_TRANSCEIVE,
1291 lpInBuffer,
1292 nInBufferSize,
1293 lpOutBuffer,
1294 nOutBufferSize);
1295 if (!NT_SUCCESS(Status) || Status == STATUS_PENDING)
1296 {
1297 BaseSetLastNTError(Status);
1298 return FALSE;
1299 }
1300
1301 if (lpBytesRead != NULL)
1302 {
1303 *lpBytesRead = lpOverlapped->InternalHigh;
1304 }
1305 }
1306 else
1307 {
1308 #if 0 /* We don't implement FSCTL_PIPE_TRANSCEIVE yet */
1309 IO_STATUS_BLOCK Iosb;
1310
1311 Status = NtFsControlFile(hNamedPipe,
1312 NULL,
1313 NULL,
1314 NULL,
1315 &Iosb,
1316 FSCTL_PIPE_TRANSCEIVE,
1317 lpInBuffer,
1318 nInBufferSize,
1319 lpOutBuffer,
1320 nOutBufferSize);
1321 if (Status == STATUS_PENDING)
1322 {
1323 Status = NtWaitForSingleObject(hNamedPipe,
1324 FALSE,
1325 NULL);
1326 if (NT_SUCCESS(Status))
1327 Status = Iosb.Status;
1328 }
1329
1330 if (NT_SUCCESS(Status))
1331 {
1332 /* lpNumberOfBytesRead must not be NULL here, in fact Win doesn't
1333 check that case either and crashes (only after the operation
1334 completed) */
1335 *lpBytesRead = Iosb.Information;
1336 }
1337 else
1338 {
1339 BaseSetLastNTError(Status);
1340 return FALSE;
1341 }
1342 #else /* Workaround while FSCTL_PIPE_TRANSCEIVE not available */
1343 DWORD nActualBytes;
1344
1345 while (0 != nInBufferSize &&
1346 WriteFile(hNamedPipe, lpInBuffer, nInBufferSize, &nActualBytes,
1347 NULL))
1348 {
1349 lpInBuffer = (LPVOID)((char *) lpInBuffer + nActualBytes);
1350 nInBufferSize -= nActualBytes;
1351 }
1352
1353 if (0 != nInBufferSize)
1354 {
1355 /* Must have dropped out of the while 'cause WriteFile failed */
1356 return FALSE;
1357 }
1358
1359 if (!ReadFile(hNamedPipe, lpOutBuffer, nOutBufferSize, &nActualBytes,
1360 NULL))
1361 {
1362 return FALSE;
1363 }
1364
1365 if (NULL != lpBytesRead)
1366 {
1367 *lpBytesRead = nActualBytes;
1368 }
1369 #endif
1370 }
1371
1372 return TRUE;
1373 }
1374
1375 /* EOF */