From 24ac223828eaca1acdce4a926ef0b5dd7124f24f Mon Sep 17 00:00:00 2001 From: Ken Brown Date: Thu, 1 Apr 2021 11:06:24 -0400 Subject: [PATCH] Cygwin: AF_UNIX: sendmsg: wait for a pipe instance in the datagram case When trying to connect to the pipe created by the remote socket, open_pipe might fail if there are no pipe instances available. This can happen if there has been a previous connection to the same pipe. The server end of the pipe won't be available again until it disconnects the previous connection. (This is done in recvmsg.) Introduce and use a new method fhandler_socket_unix::wait_open_pipe that waits (in the blocking case) for an instance to become available. --- winsup/cygwin/fhandler.h | 1 + winsup/cygwin/fhandler_socket_unix.cc | 87 ++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index e474203eb..932f535e8 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1186,6 +1186,7 @@ class fhandler_socket_unix : public fhandler_socket HANDLE create_pipe (bool single_instance); HANDLE create_pipe_instance (); NTSTATUS open_pipe (HANDLE &ph, PUNICODE_STRING pipe_name); + int wait_open_pipe (HANDLE &ph, PUNICODE_STRING pipe_name); void xchg_sock_info (); int wait_pipe (PUNICODE_STRING pipe_name); int connect_pipe (PUNICODE_STRING pipe_name); diff --git a/winsup/cygwin/fhandler_socket_unix.cc b/winsup/cygwin/fhandler_socket_unix.cc index 1a0a7e7ba..dc71a8d2a 100644 --- a/winsup/cygwin/fhandler_socket_unix.cc +++ b/winsup/cygwin/fhandler_socket_unix.cc @@ -1479,6 +1479,81 @@ out: return error; } +/* A variant of wait_pipe_thread, called by sendmsg in datagram case. + Wait for a pipe instance to become available and connect to it. */ +int +fhandler_socket_unix::wait_open_pipe (HANDLE &ph, PUNICODE_STRING pipe_name) +{ + HANDLE npfsh; + HANDLE evt; + NTSTATUS status; + IO_STATUS_BLOCK io; + ULONG pwbuf_size; + PFILE_PIPE_WAIT_FOR_BUFFER pwbuf; + int ret = -1; + + status = npfs_handle (npfsh); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + goto out; + } + if (!(evt = create_event ())) + goto out; + pwbuf_size = offsetof (FILE_PIPE_WAIT_FOR_BUFFER, Name) + pipe_name->Length; + pwbuf = (PFILE_PIPE_WAIT_FOR_BUFFER) alloca (pwbuf_size); + pwbuf->NameLength = pipe_name->Length; + pwbuf->TimeoutSpecified = FALSE; + memcpy (pwbuf->Name, pipe_name->Buffer, pipe_name->Length); + retry: + status = NtFsControlFile (npfsh, evt, NULL, NULL, &io, FSCTL_PIPE_WAIT, + pwbuf, pwbuf_size, NULL, 0); + if (status == STATUS_PENDING) + { + switch (cygwait (evt, cw_infinite, cw_cancel | cw_sig_eintr)) + { + case WAIT_OBJECT_0: + status = io.Status; + break; + case WAIT_CANCELED: + pthread::static_cancel_self (); + /*NOTREACHED*/ + case WAIT_SIGNALED: + set_errno (EINTR); + break; + default: + break; + } + } + switch (status) + { + case STATUS_SUCCESS: + status = open_pipe (ph, pipe_name); + if (NT_SUCCESS (status)) + ret = 0; + else if (STATUS_PIPE_NO_INSTANCE_AVAILABLE (status)) + /* Someone else grabbed the pipe instance. */ + goto retry; + else + __seterrno_from_nt_status (status); + break; + case STATUS_OBJECT_NAME_NOT_FOUND: + set_errno (EADDRNOTAVAIL); + break; + case STATUS_INSUFFICIENT_RESOURCES: + set_errno (ENOBUFS); + break; + case STATUS_INVALID_DEVICE_REQUEST: + default: + set_errno (EIO); + break; + } + out: + if (evt) + NtClose (evt); + return ret; +} + int fhandler_socket_unix::socket (int af, int type, int protocol, int flags) { @@ -3106,7 +3181,17 @@ fhandler_socket_unix::sendmsg (const struct msghdr *msg, int flags) __leave; } status = open_pipe (ph, &pipe_name); - if (!NT_SUCCESS (status)) + if (STATUS_PIPE_NO_INSTANCE_AVAILABLE (status)) + { + if (is_nonblocking () || flags & MSG_DONTWAIT) + { + set_errno (EAGAIN); + __leave; + } + if (wait_open_pipe (ph, &pipe_name) < 0) + __leave; + } + else if (!NT_SUCCESS (status)) { __seterrno_from_nt_status (status); __leave;