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