Cygwin: AF_UNIX: Implement listen, accept4, connect, and others
* Implement helper functions * Improve bind * Implement setting blocking, ioctl(FIONBIO), fcntl(F_SETFL) * Implement close_on_exec and fixup_after_fork * Allow overriding sun_path and peer_sun_path * Improve comments Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
e2909e2805
commit
4cd57934be
@ -857,28 +857,43 @@ class fhandler_socket_unix : public fhandler_socket
|
||||
NPFS_DEVICE,
|
||||
NPFS_DIR
|
||||
};
|
||||
protected:
|
||||
HANDLE file; /* Either NT symlink or reparse point */
|
||||
|
||||
HANDLE create_abstract_link (const sun_name_t *sun,
|
||||
PUNICODE_STRING pipe_name);
|
||||
HANDLE create_reparse_point (const sun_name_t *sun,
|
||||
PUNICODE_STRING pipe_name);
|
||||
HANDLE create_file (const sun_name_t *sun);
|
||||
int open_abstract_link (sun_name_t *sun, PUNICODE_STRING pipe_name);
|
||||
int open_reparse_point (sun_name_t *sun, PUNICODE_STRING pipe_name);
|
||||
int open_file (sun_name_t *sun, int &type, PUNICODE_STRING pipe_name);
|
||||
HANDLE autobind (sun_name_t *sun);
|
||||
wchar_t get_type_char ();
|
||||
void gen_pipe_name ();
|
||||
void set_wait_state (DWORD wait_state);
|
||||
HANDLE create_pipe ();
|
||||
HANDLE create_pipe_instance ();
|
||||
|
||||
protected:
|
||||
SRWLOCK conn_lock;
|
||||
SRWLOCK bind_lock;
|
||||
SRWLOCK io_lock;
|
||||
HANDLE backing_file_handle; /* Either NT symlink or INVALID_HANDLE_VALUE,
|
||||
if the socket is backed by a file in the
|
||||
file system (actually a reparse point) */
|
||||
HANDLE connect_wait_thr;
|
||||
PVOID cwt_param;
|
||||
LONG so_error;
|
||||
sun_name_t *sun_path;
|
||||
sun_name_t *peer_sun_path;
|
||||
struct ucred peer_cred;
|
||||
|
||||
void gen_pipe_name ();
|
||||
static HANDLE create_abstract_link (const sun_name_t *sun,
|
||||
PUNICODE_STRING pipe_name);
|
||||
static HANDLE create_reparse_point (const sun_name_t *sun,
|
||||
PUNICODE_STRING pipe_name);
|
||||
HANDLE create_file (const sun_name_t *sun);
|
||||
static int open_abstract_link (sun_name_t *sun, PUNICODE_STRING pipe_name);
|
||||
static int open_reparse_point (sun_name_t *sun, PUNICODE_STRING pipe_name);
|
||||
static int open_file (sun_name_t *sun, int &type, PUNICODE_STRING pipe_name);
|
||||
HANDLE autobind (sun_name_t *sun);
|
||||
wchar_t get_type_char ();
|
||||
void set_pipe_non_blocking (bool nonblocking);
|
||||
int send_my_name ();
|
||||
int recv_peer_name ();
|
||||
static NTSTATUS npfs_handle (HANDLE &nph, npfs_hdl_t type);
|
||||
HANDLE create_pipe ();
|
||||
HANDLE create_pipe_instance ();
|
||||
NTSTATUS open_pipe (HANDLE &ph, PUNICODE_STRING pipe_name);
|
||||
int wait_pipe (PUNICODE_STRING pipe_name);
|
||||
int connect_pipe (PUNICODE_STRING pipe_name);
|
||||
int listen_pipe ();
|
||||
int disconnect_pipe (HANDLE ph);
|
||||
sun_name_t *get_sun_path () {return sun_path;}
|
||||
sun_name_t *get_peer_sun_path () {return peer_sun_path;}
|
||||
void set_sun_path (struct sockaddr_un *un, __socklen_t unlen);
|
||||
@ -888,10 +903,9 @@ class fhandler_socket_unix : public fhandler_socket
|
||||
void set_peer_sun_path (sun_name_t *snt)
|
||||
{ snt ? set_peer_sun_path (&snt->un, snt->un_len)
|
||||
: set_peer_sun_path (NULL, 0); }
|
||||
|
||||
protected:
|
||||
struct ucred peer_cred;
|
||||
void set_cred ();
|
||||
void fixup_after_fork (HANDLE parent);
|
||||
void set_close_on_exec (bool val);
|
||||
|
||||
public:
|
||||
fhandler_socket_unix ();
|
||||
@ -899,6 +913,8 @@ class fhandler_socket_unix : public fhandler_socket
|
||||
|
||||
int dup (fhandler_base *child, int);
|
||||
|
||||
DWORD wait_pipe_thread (PUNICODE_STRING pipe_name);
|
||||
|
||||
int socket (int af, int type, int protocol, int flags);
|
||||
int socketpair (int af, int type, int protocol, int flags,
|
||||
fhandler_socket *fh_out);
|
||||
|
@ -60,7 +60,70 @@
|
||||
Note: We use MAX_PATH below for convenience where sufficient. It's
|
||||
big enough to hold sun_paths as well as pipe names so we don't have
|
||||
to use tmp_pathbuf as often.
|
||||
|
||||
Every packet sent to a peer is a combination of the socket name of the
|
||||
local socket, the ancillary data, and the actual user data. The data
|
||||
is always sent in this order. The header contains length information
|
||||
for the entire packet, as well as for all three data blocks. The
|
||||
combined maximum size of a packet is 64K, including the header.
|
||||
|
||||
A connecting, bound STREAM socket send it's local sun_path once after
|
||||
a successful connect. An already connected socket also sends its local
|
||||
sun_path after a successful bind (border case, but still...). These
|
||||
packages don't contain any other data (cmsg_len == 0, data_len == 0).
|
||||
|
||||
A bound DGRAM socket send its sun_path with each sendmsg/sendto.
|
||||
*/
|
||||
class af_unix_pkt_hdr_t
|
||||
{
|
||||
public:
|
||||
uint16_t pckt_len; /* size of packet including header */
|
||||
uint8_t shut_info; /* shutdown info. SHUT_RD means
|
||||
SHUT_RD on the local side, so the
|
||||
peer must not send further packets,
|
||||
vice versa for SHUT_WR. SHUT_RDWR
|
||||
is followed by closing the pipe
|
||||
handle. */
|
||||
uint8_t name_len; /* size of name, a sockaddr_un */
|
||||
uint16_t cmsg_len; /* size of ancillary data block */
|
||||
uint16_t data_len; /* size of user data */
|
||||
|
||||
void init (uint8_t s, uint8_t n, uint16_t c, uint16_t d)
|
||||
{
|
||||
shut_info = s;
|
||||
name_len = n;
|
||||
cmsg_len = c;
|
||||
data_len = d;
|
||||
pckt_len = sizeof (*this) + name_len + cmsg_len + data_len;
|
||||
}
|
||||
};
|
||||
|
||||
#define AF_UNIX_PKT_OFFSETOF_NAME(phdr) \
|
||||
(sizeof (af_unix_pkt_hdr_t))
|
||||
#define AF_UNIX_PKT_OFFSETOF_CMSG(phdr) \
|
||||
(sizeof (af_unix_pkt_hdr_t) + (phdr)->name_len)
|
||||
#define AF_UNIX_PKT_OFFSETOF_DATA(phdr) \
|
||||
({ \
|
||||
af_unix_pkt_hdr_t *_p = phdr; \
|
||||
sizeof (af_unix_pkt_hdr_t) + (_p)->name_len + (_p)->cmsg_len; \
|
||||
})
|
||||
#define AF_UNIX_PKT_NAME(phdr) \
|
||||
({ \
|
||||
af_unix_pkt_hdr_t *_p = phdr; \
|
||||
(struct sockaddr_un *)(((PBYTE)(_p)) \
|
||||
+ AF_UNIX_PKT_OFFSETOF_NAME (_p)); \
|
||||
})
|
||||
#define AF_UNIX_PKT_CMSG(phdr) \
|
||||
({ \
|
||||
af_unix_pkt_hdr_t *_p = phdr; \
|
||||
(void *)(((PBYTE)(_p)) + AF_UNIX_PKT_OFFSETOF_CMSG (_p)); \
|
||||
})
|
||||
#define AF_UNIX_PKT_DATA(phdr) \
|
||||
({ \
|
||||
af_unix_pkt_hdr_t _p = phdr; \
|
||||
(void *)(((PBYTE)(_p)) + AF_UNIX_PKT_OFFSETOF_DATA (_p)); \
|
||||
})
|
||||
|
||||
/* Character length of pipe name, excluding trailing NUL. */
|
||||
#define CYGWIN_PIPE_SOCKET_NAME_LEN 47
|
||||
|
||||
@ -439,7 +502,9 @@ fhandler_socket_unix::get_type_char ()
|
||||
}
|
||||
}
|
||||
|
||||
/* This also sets the pipe to message mode unconditionally. */
|
||||
void
|
||||
fhandler_socket_unix::set_pipe_non_blocking (bool nonblocking)
|
||||
{
|
||||
if (get_handle ())
|
||||
{
|
||||
@ -448,11 +513,94 @@ void
|
||||
FILE_PIPE_INFORMATION fpi;
|
||||
|
||||
fpi.ReadMode = FILE_PIPE_MESSAGE_MODE;
|
||||
fpi.CompletionMode = wait_state;
|
||||
fpi.CompletionMode = nonblocking ? FILE_PIPE_COMPLETE_OPERATION
|
||||
: FILE_PIPE_QUEUE_OPERATION;
|
||||
status = NtSetInformationFile (get_handle (), &io, &fpi, sizeof fpi,
|
||||
FilePipeInformation);
|
||||
if (!NT_SUCCESS (status))
|
||||
system_printf ("NtSetInformationFile(FilePipeInformation): %y", status);
|
||||
debug_printf ("NtSetInformationFile(FilePipeInformation): %y", status);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::send_my_name ()
|
||||
{
|
||||
size_t name_len = 0;
|
||||
af_unix_pkt_hdr_t *packet;
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK io;
|
||||
|
||||
AcquireSRWLockShared (&bind_lock);
|
||||
name_len = get_sun_path ()->un_len;
|
||||
packet = (af_unix_pkt_hdr_t *) alloca (sizeof *packet + name_len);
|
||||
memcpy (AF_UNIX_PKT_NAME (packet), &get_sun_path ()->un, name_len);
|
||||
ReleaseSRWLockShared (&bind_lock);
|
||||
|
||||
packet->init (0, name_len, 0, 0);
|
||||
|
||||
/* The theory: Fire and forget. */
|
||||
AcquireSRWLockExclusive (&io_lock);
|
||||
set_pipe_non_blocking (true);
|
||||
status = NtWriteFile (get_handle (), NULL, NULL, NULL, &io, packet,
|
||||
packet->pckt_len, NULL, NULL);
|
||||
set_pipe_non_blocking (is_nonblocking ());
|
||||
ReleaseSRWLockExclusive (&io_lock);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
debug_printf ("Couldn't send my name: NtWriteFile: %y", status);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns an error code. Locking is not required, user space doesn't know
|
||||
about this socket yet. */
|
||||
int
|
||||
fhandler_socket_unix::recv_peer_name ()
|
||||
{
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK io;
|
||||
af_unix_pkt_hdr_t *packet;
|
||||
struct sockaddr_un *un;
|
||||
ULONG len;
|
||||
int ret = 0;
|
||||
|
||||
len = sizeof *packet + sizeof *un;
|
||||
packet = (af_unix_pkt_hdr_t *) alloca (len);
|
||||
set_pipe_non_blocking (false);
|
||||
status = NtReadFile (get_handle (), NULL, NULL, NULL, &io, packet, len,
|
||||
NULL, NULL);
|
||||
if (status == STATUS_PENDING)
|
||||
{
|
||||
DWORD ret;
|
||||
LARGE_INTEGER timeout;
|
||||
|
||||
timeout.QuadPart = -20 * NS100PERSEC; /* 20 secs */
|
||||
ret = cygwait (connect_wait_thr, &timeout, cw_sig_eintr);
|
||||
switch (ret)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
status = io.Status;
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
ret = ECONNABORTED;
|
||||
break;
|
||||
case WAIT_SIGNALED:
|
||||
ret = EINTR;
|
||||
break;
|
||||
default:
|
||||
ret = EPROTO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!NT_SUCCESS (status) && ret == 0)
|
||||
ret = geterrno_from_nt_status (status);
|
||||
if (ret == 0 && packet->name_len > 0)
|
||||
set_peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len);
|
||||
set_pipe_non_blocking (is_nonblocking ());
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
fhandler_socket_unix::npfs_handle (HANDLE &nph, npfs_hdl_t type)
|
||||
{
|
||||
@ -562,24 +710,189 @@ fhandler_socket_unix::create_pipe_instance ()
|
||||
return ph;
|
||||
}
|
||||
|
||||
fhandler_socket_unix::fhandler_socket_unix () :
|
||||
sun_path (NULL),
|
||||
peer_sun_path (NULL)
|
||||
NTSTATUS
|
||||
fhandler_socket_unix::open_pipe (HANDLE &ph, PUNICODE_STRING pipe_name)
|
||||
{
|
||||
set_cred ();
|
||||
NTSTATUS status;
|
||||
HANDLE npfsh;
|
||||
ACCESS_MASK access;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
IO_STATUS_BLOCK io;
|
||||
ULONG sharing;
|
||||
|
||||
status = npfs_handle (npfsh, NPFS_DIR);
|
||||
if (!NT_SUCCESS (status))
|
||||
return status;
|
||||
access = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE;
|
||||
InitializeObjectAttributes (&attr, pipe_name, OBJ_INHERIT, npfsh, NULL);
|
||||
sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
status = NtOpenFile (&ph, access, &attr, &io, sharing, 0);
|
||||
if (NT_SUCCESS (status))
|
||||
{
|
||||
set_io_handle (ph);
|
||||
send_my_name ();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
fhandler_socket_unix::~fhandler_socket_unix ()
|
||||
struct conn_wait_info_t
|
||||
{
|
||||
if (sun_path)
|
||||
delete sun_path;
|
||||
if (peer_sun_path)
|
||||
delete peer_sun_path;
|
||||
fhandler_socket_unix *fh;
|
||||
UNICODE_STRING pipe_name;
|
||||
WCHAR pipe_name_buf[CYGWIN_PIPE_SOCKET_NAME_LEN + 1];
|
||||
};
|
||||
|
||||
/* Just hop to the wait_pipe_thread method. */
|
||||
DWORD WINAPI
|
||||
connect_wait_func (LPVOID param)
|
||||
{
|
||||
conn_wait_info_t *wait_info = (conn_wait_info_t *) param;
|
||||
return wait_info->fh->wait_pipe_thread (&wait_info->pipe_name);
|
||||
}
|
||||
|
||||
/* Start a waiter thread to wait for a pipe instance to become available.
|
||||
in blocking mode, wait for the thread to finish. In nonblocking mode
|
||||
just return with errno set to EINPROGRESS. */
|
||||
int
|
||||
fhandler_socket_unix::wait_pipe (PUNICODE_STRING pipe_name)
|
||||
{
|
||||
conn_wait_info_t *wait_info;
|
||||
DWORD waitret, err;
|
||||
int ret = -1;
|
||||
|
||||
wait_info = (conn_wait_info_t *)
|
||||
cmalloc_abort (HEAP_FHANDLER, sizeof *wait_info);
|
||||
wait_info->fh = this;
|
||||
RtlInitEmptyUnicodeString (&wait_info->pipe_name, wait_info->pipe_name_buf,
|
||||
sizeof wait_info->pipe_name_buf);
|
||||
RtlCopyUnicodeString (&wait_info->pipe_name, pipe_name);
|
||||
|
||||
cwt_param = (PVOID) wait_info;
|
||||
connect_wait_thr = CreateThread (NULL, PREFERRED_IO_BLKSIZE,
|
||||
connect_wait_func, cwt_param, 0, NULL);
|
||||
if (!connect_wait_thr)
|
||||
{
|
||||
cfree (wait_info);
|
||||
__seterrno ();
|
||||
return -1;
|
||||
}
|
||||
if (is_nonblocking ())
|
||||
{
|
||||
set_errno (EINPROGRESS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
waitret = cygwait (connect_wait_thr, cw_infinite, cw_cancel | cw_sig_eintr);
|
||||
if (waitret == WAIT_OBJECT_0)
|
||||
GetExitCodeThread (connect_wait_thr, &err);
|
||||
else
|
||||
TerminateThread (connect_wait_thr, 0);
|
||||
HANDLE thr = InterlockedExchangePointer (&connect_wait_thr, NULL);
|
||||
if (thr)
|
||||
CloseHandle (thr);
|
||||
PVOID param = InterlockedExchangePointer (&cwt_param, NULL);
|
||||
if (param)
|
||||
cfree (param);
|
||||
switch (waitret)
|
||||
{
|
||||
case WAIT_CANCELED:
|
||||
pthread::static_cancel_self ();
|
||||
/*NOTREACHED*/
|
||||
case WAIT_SIGNALED:
|
||||
set_errno (EINTR);
|
||||
break;
|
||||
default:
|
||||
InterlockedExchange (&so_error, err);
|
||||
if (err)
|
||||
set_errno (err);
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::connect_pipe (PUNICODE_STRING pipe_name)
|
||||
{
|
||||
NTSTATUS status;
|
||||
HANDLE ph = NULL;
|
||||
|
||||
/* Try connecting first. If it doesn't work, wait for the pipe
|
||||
to become available. */
|
||||
status = open_pipe (ph, pipe_name);
|
||||
if (status == STATUS_PIPE_BUSY)
|
||||
return wait_pipe (pipe_name);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
InterlockedExchange (&so_error, get_errno ());
|
||||
return -1;
|
||||
}
|
||||
InterlockedExchange (&so_error, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::listen_pipe ()
|
||||
{
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK io;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
HANDLE evt = NULL;
|
||||
|
||||
io.Status = STATUS_PENDING;
|
||||
if (!is_nonblocking ())
|
||||
{
|
||||
/* Create event object and set APC context pointer. */
|
||||
InitializeObjectAttributes (&attr, NULL, 0, NULL, NULL);
|
||||
status = NtCreateEvent (&evt, EVENT_ALL_ACCESS, &attr,
|
||||
NotificationEvent, FALSE);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
status = NtFsControlFile (get_handle (), evt, NULL, NULL, &io,
|
||||
FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0);
|
||||
if (status == STATUS_PENDING)
|
||||
{
|
||||
if (cygwait (evt ?: get_handle ()) == WAIT_OBJECT_0)
|
||||
status = io.Status;
|
||||
}
|
||||
if (evt)
|
||||
NtClose (evt);
|
||||
if (status == STATUS_PIPE_LISTENING)
|
||||
set_errno (EAGAIN);
|
||||
else if (status != STATUS_PIPE_CONNECTED)
|
||||
__seterrno_from_nt_status (status);
|
||||
return (status == STATUS_PIPE_CONNECTED) ? 0 : -1;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::disconnect_pipe (HANDLE ph)
|
||||
{
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK io;
|
||||
|
||||
status = NtFsControlFile (ph, NULL, NULL, NULL, &io, FSCTL_PIPE_DISCONNECT,
|
||||
NULL, 0, NULL, 0);
|
||||
if (status == STATUS_PENDING && cygwait (ph) == WAIT_OBJECT_0)
|
||||
status = io.Status;
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
__seterrno_from_nt_status (status);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fhandler_socket_unix::set_sun_path (struct sockaddr_un *un, socklen_t unlen)
|
||||
{
|
||||
if (peer_sun_path)
|
||||
delete peer_sun_path;
|
||||
if (!un)
|
||||
sun_path = NULL;
|
||||
sun_path = new sun_name_t ((const struct sockaddr *) un, unlen);
|
||||
@ -589,6 +902,8 @@ void
|
||||
fhandler_socket_unix::set_peer_sun_path (struct sockaddr_un *un,
|
||||
socklen_t unlen)
|
||||
{
|
||||
if (peer_sun_path)
|
||||
delete peer_sun_path;
|
||||
if (!un)
|
||||
peer_sun_path = NULL;
|
||||
peer_sun_path = new sun_name_t ((const struct sockaddr *) un, unlen);
|
||||
@ -602,15 +917,143 @@ fhandler_socket_unix::set_cred ()
|
||||
peer_cred.gid = (gid_t) -1;
|
||||
}
|
||||
|
||||
void
|
||||
fhandler_socket_unix::fixup_after_fork (HANDLE parent)
|
||||
{
|
||||
fhandler_socket::fixup_after_fork (parent);
|
||||
if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE)
|
||||
fork_fixup (parent, backing_file_handle, "backing_file_handle");
|
||||
InitializeSRWLock (&conn_lock);
|
||||
InitializeSRWLock (&bind_lock);
|
||||
InitializeSRWLock (&io_lock);
|
||||
connect_wait_thr = NULL;
|
||||
cwt_param = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
fhandler_socket_unix::set_close_on_exec (bool val)
|
||||
{
|
||||
fhandler_base::set_close_on_exec (val);
|
||||
if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE)
|
||||
set_no_inheritance (backing_file_handle, val);
|
||||
}
|
||||
|
||||
/* ========================== public methods ========================= */
|
||||
|
||||
fhandler_socket_unix::fhandler_socket_unix ()
|
||||
{
|
||||
set_cred ();
|
||||
}
|
||||
|
||||
fhandler_socket_unix::~fhandler_socket_unix ()
|
||||
{
|
||||
if (sun_path)
|
||||
delete sun_path;
|
||||
if (peer_sun_path)
|
||||
delete peer_sun_path;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::dup (fhandler_base *child, int flags)
|
||||
{
|
||||
fhandler_socket_unix *fhs = (fhandler_socket_unix *) child;
|
||||
fhs->set_sun_path (get_sun_path ());
|
||||
fhs->set_peer_sun_path (get_peer_sun_path ());
|
||||
InitializeSRWLock (&fhs->conn_lock);
|
||||
InitializeSRWLock (&fhs->bind_lock);
|
||||
InitializeSRWLock (&fhs->io_lock);
|
||||
fhs->connect_wait_thr = NULL;
|
||||
fhs->cwt_param = NULL;
|
||||
return fhandler_socket::dup (child, flags);
|
||||
}
|
||||
|
||||
/* Waiter thread method. Here we wait for a pipe instance to become
|
||||
available and connect to it, if so. This function is running
|
||||
asynchronously if called on a non-blocking pipe. The important
|
||||
things to do:
|
||||
|
||||
- Set the peer pipe handle if successful
|
||||
- Send own sun_path to peer if successful TODO
|
||||
- Set connect_state
|
||||
- Set so_error for later call to select
|
||||
*/
|
||||
DWORD
|
||||
fhandler_socket_unix::wait_pipe_thread (PUNICODE_STRING pipe_name)
|
||||
{
|
||||
HANDLE npfsh;
|
||||
LONG error = 0;
|
||||
NTSTATUS status;
|
||||
IO_STATUS_BLOCK io;
|
||||
ULONG pwbuf_size;
|
||||
PFILE_PIPE_WAIT_FOR_BUFFER pwbuf;
|
||||
LONGLONG stamp;
|
||||
HANDLE ph = NULL;
|
||||
|
||||
status = npfs_handle (npfsh, NPFS_DEVICE);
|
||||
if (!NT_SUCCESS (status))
|
||||
{
|
||||
error = geterrno_from_nt_status (status);
|
||||
goto out;
|
||||
}
|
||||
pwbuf_size = offsetof (FILE_PIPE_WAIT_FOR_BUFFER, Name) + pipe_name->Length;
|
||||
pwbuf = (PFILE_PIPE_WAIT_FOR_BUFFER) alloca (pwbuf_size);
|
||||
pwbuf->Timeout.QuadPart = -20 * NS100PERSEC; /* 20 secs */
|
||||
pwbuf->NameLength = pipe_name->Length;
|
||||
pwbuf->TimeoutSpecified = TRUE;
|
||||
memcpy (pwbuf->Name, pipe_name->Buffer, pipe_name->Length);
|
||||
stamp = ntod.nsecs ();
|
||||
do
|
||||
{
|
||||
status = NtFsControlFile (npfsh, NULL, NULL, NULL, &io, FSCTL_PIPE_WAIT,
|
||||
pwbuf, pwbuf_size, NULL, 0);
|
||||
switch (status)
|
||||
{
|
||||
case STATUS_SUCCESS:
|
||||
{
|
||||
status = open_pipe (ph, pipe_name);
|
||||
if (status == STATUS_PIPE_BUSY)
|
||||
{
|
||||
/* Another concurrent connect grabbed the pipe instance
|
||||
under our nose. Fix the timeout value and go waiting
|
||||
again, unless the timeout has passed. */
|
||||
pwbuf->Timeout.QuadPart -= (stamp - ntod.nsecs ()) / 100LL;
|
||||
if (pwbuf->Timeout.QuadPart >= 0)
|
||||
{
|
||||
status = STATUS_IO_TIMEOUT;
|
||||
error = ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
else if (!NT_SUCCESS (status))
|
||||
error = geterrno_from_nt_status (status);
|
||||
}
|
||||
break;
|
||||
case STATUS_OBJECT_NAME_NOT_FOUND:
|
||||
error = EADDRNOTAVAIL;
|
||||
break;
|
||||
case STATUS_IO_TIMEOUT:
|
||||
error = ETIMEDOUT;
|
||||
break;
|
||||
case STATUS_INSUFFICIENT_RESOURCES:
|
||||
error = ENOBUFS;
|
||||
break;
|
||||
case STATUS_INVALID_DEVICE_REQUEST:
|
||||
default:
|
||||
error = EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (status == STATUS_PIPE_BUSY);
|
||||
out:
|
||||
PVOID param = InterlockedExchangePointer (&cwt_param, NULL);
|
||||
if (param)
|
||||
cfree (param);
|
||||
AcquireSRWLockExclusive (&conn_lock);
|
||||
InterlockedExchange (&so_error, error);
|
||||
connect_state (error ? connect_failed : connected);
|
||||
ReleaseSRWLockExclusive (&conn_lock);
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::socket (int af, int type, int protocol, int flags)
|
||||
{
|
||||
@ -656,6 +1099,9 @@ fhandler_socket_unix::socketpair (int af, int type, int protocol, int flags,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Bind creates the backing file, generates the pipe name and sets
|
||||
bind_state. On DGRAM sockets it also creates the pipe. On STREAM
|
||||
sockets either listen or connect will do that. */
|
||||
int
|
||||
fhandler_socket_unix::bind (const struct sockaddr *name, int namelen)
|
||||
{
|
||||
@ -663,46 +1109,253 @@ fhandler_socket_unix::bind (const struct sockaddr *name, int namelen)
|
||||
bool unnamed = (sun.un_len == sizeof sun.un.sun_family);
|
||||
HANDLE pipe = NULL;
|
||||
|
||||
/* If we have a handle, we're already bound. */
|
||||
if (get_handle () || sun.un.sun_family != AF_UNIX)
|
||||
if (sun.un.sun_family != AF_UNIX)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
gen_pipe_name ();
|
||||
pipe = create_pipe ();
|
||||
if (!pipe)
|
||||
return -1;
|
||||
file = unnamed ? autobind (&sun) : create_file (&sun);
|
||||
if (!file)
|
||||
AcquireSRWLockExclusive (&bind_lock);
|
||||
if (binding_state () == bind_pending)
|
||||
{
|
||||
NtClose (pipe);
|
||||
set_errno (EALREADY);
|
||||
ReleaseSRWLockExclusive (&bind_lock);
|
||||
return -1;
|
||||
}
|
||||
if (binding_state () == bound)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
ReleaseSRWLockExclusive (&bind_lock);
|
||||
return -1;
|
||||
}
|
||||
binding_state (bind_pending);
|
||||
ReleaseSRWLockExclusive (&bind_lock);
|
||||
gen_pipe_name ();
|
||||
if (get_socket_type () == SOCK_DGRAM)
|
||||
{
|
||||
pipe = create_pipe ();
|
||||
if (!pipe)
|
||||
{
|
||||
binding_state (unbound);
|
||||
return -1;
|
||||
}
|
||||
set_io_handle (pipe);
|
||||
}
|
||||
backing_file_handle = unnamed ? autobind (&sun) : create_file (&sun);
|
||||
if (!backing_file_handle)
|
||||
{
|
||||
set_io_handle (NULL);
|
||||
if (pipe)
|
||||
NtClose (pipe);
|
||||
binding_state (unbound);
|
||||
return -1;
|
||||
}
|
||||
set_io_handle (pipe);
|
||||
set_sun_path (&sun);
|
||||
/* If we're already connected, send name to peer. */
|
||||
if (connect_state () == connected)
|
||||
send_my_name ();
|
||||
binding_state (bound);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create pipe on non-DGRAM sockets and set conn_state to listener. */
|
||||
int
|
||||
fhandler_socket_unix::listen (int backlog)
|
||||
{
|
||||
set_errno (EAFNOSUPPORT);
|
||||
return -1;
|
||||
if (get_socket_type () == SOCK_DGRAM)
|
||||
{
|
||||
set_errno (EOPNOTSUPP);
|
||||
return -1;
|
||||
}
|
||||
AcquireSRWLockShared (&bind_lock);
|
||||
while (binding_state () == bind_pending)
|
||||
yield ();
|
||||
if (binding_state () == unbound)
|
||||
{
|
||||
set_errno (EDESTADDRREQ);
|
||||
ReleaseSRWLockShared (&bind_lock);
|
||||
return -1;
|
||||
}
|
||||
ReleaseSRWLockShared (&bind_lock);
|
||||
AcquireSRWLockExclusive (&conn_lock);
|
||||
if (connect_state () != unconnected && connect_state () != connect_failed)
|
||||
{
|
||||
set_errno (connect_state () == listener ? EADDRINUSE : EINVAL);
|
||||
ReleaseSRWLockExclusive (&conn_lock);
|
||||
return -1;
|
||||
}
|
||||
if (get_socket_type () != SOCK_DGRAM)
|
||||
{
|
||||
HANDLE pipe = create_pipe ();
|
||||
if (!pipe)
|
||||
{
|
||||
connect_state (unconnected);
|
||||
return -1;
|
||||
}
|
||||
set_io_handle (pipe);
|
||||
}
|
||||
connect_state (listener);
|
||||
ReleaseSRWLockExclusive (&conn_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
|
||||
{
|
||||
set_errno (EAFNOSUPPORT);
|
||||
if (get_socket_type () != SOCK_STREAM)
|
||||
{
|
||||
set_errno (EOPNOTSUPP);
|
||||
return -1;
|
||||
}
|
||||
if (connect_state () != listener
|
||||
|| (peer && (!len || *len < (int) sizeof (sa_family_t))))
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if (listen_pipe () == 0)
|
||||
{
|
||||
/* Our handle is now connected with a client. This handle is used
|
||||
for the accepted socket. Our handle has to be replaced with a
|
||||
new instance handle for the next accept. */
|
||||
HANDLE accepted = get_handle ();
|
||||
HANDLE new_inst = create_pipe_instance ();
|
||||
int error = ENOBUFS;
|
||||
if (new_inst)
|
||||
{
|
||||
/* Set new io handle. */
|
||||
set_io_handle (new_inst);
|
||||
/* Prepare new file descriptor. */
|
||||
cygheap_fdnew fd;
|
||||
|
||||
if (fd >= 0)
|
||||
{
|
||||
fhandler_socket_unix *sock = (fhandler_socket_unix *)
|
||||
build_fh_dev (dev ());
|
||||
if (sock)
|
||||
{
|
||||
sock->set_addr_family (get_addr_family ());
|
||||
sock->set_socket_type (get_socket_type ());
|
||||
if (flags & SOCK_NONBLOCK)
|
||||
sock->set_nonblocking (true);
|
||||
if (flags & SOCK_CLOEXEC)
|
||||
sock->set_close_on_exec (true);
|
||||
sock->set_unique_id ();
|
||||
sock->set_ino (sock->get_unique_id ());
|
||||
sock->pc.set_nt_native_path (pc.get_nt_native_path ());
|
||||
sock->connect_state (connected);
|
||||
sock->binding_state (binding_state ());
|
||||
sock->set_io_handle (accepted);
|
||||
|
||||
sock->set_sun_path (get_sun_path ());
|
||||
error = sock->recv_peer_name ();
|
||||
if (error == 0)
|
||||
{
|
||||
if (peer)
|
||||
{
|
||||
sun_name_t *sun = sock->get_peer_sun_path ();
|
||||
if (sun)
|
||||
{
|
||||
memcpy (peer, &sun->un, MIN (*len, sun->un_len));
|
||||
*len = sun->un_len;
|
||||
}
|
||||
else if (len)
|
||||
*len = 0;
|
||||
}
|
||||
fd = sock;
|
||||
if (fd <= 2)
|
||||
set_std_handle (fd);
|
||||
return fd;
|
||||
}
|
||||
delete sock;
|
||||
}
|
||||
fd.release ();
|
||||
}
|
||||
}
|
||||
/* Ouch! We can't handle the client if we couldn't
|
||||
create a new instance to accept more connections.*/
|
||||
disconnect_pipe (accepted);
|
||||
set_errno (error);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::connect (const struct sockaddr *name, int namelen)
|
||||
{
|
||||
set_errno (EAFNOSUPPORT);
|
||||
return -1;
|
||||
sun_name_t sun (name, namelen);
|
||||
int peer_type;
|
||||
WCHAR pipe_name_buf[CYGWIN_PIPE_SOCKET_NAME_LEN + 1];
|
||||
UNICODE_STRING pipe_name;
|
||||
|
||||
/* Test and set connection state. */
|
||||
AcquireSRWLockExclusive (&conn_lock);
|
||||
if (connect_state () == connect_pending)
|
||||
{
|
||||
set_errno (EALREADY);
|
||||
ReleaseSRWLockExclusive (&conn_lock);
|
||||
return -1;
|
||||
}
|
||||
if (connect_state () == listener)
|
||||
{
|
||||
set_errno (EADDRINUSE);
|
||||
ReleaseSRWLockExclusive (&conn_lock);
|
||||
return -1;
|
||||
}
|
||||
if (connect_state () == connected && get_socket_type () != SOCK_DGRAM)
|
||||
{
|
||||
set_errno (EISCONN);
|
||||
ReleaseSRWLockExclusive (&conn_lock);
|
||||
return -1;
|
||||
}
|
||||
connect_state (connect_pending);
|
||||
ReleaseSRWLockExclusive (&conn_lock);
|
||||
/* Check validity of name */
|
||||
if (sun.un_len <= (int) sizeof (sa_family_t))
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
connect_state (unconnected);
|
||||
return -1;
|
||||
}
|
||||
if (sun.un.sun_family != AF_UNIX)
|
||||
{
|
||||
set_errno (EAFNOSUPPORT);
|
||||
connect_state (unconnected);
|
||||
return -1;
|
||||
}
|
||||
if (sun.un_len == 3 && sun.un.sun_path[0] == '\0')
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
connect_state (unconnected);
|
||||
return -1;
|
||||
}
|
||||
/* Check if peer address exists. */
|
||||
RtlInitEmptyUnicodeString (&pipe_name, pipe_name_buf, sizeof pipe_name_buf);
|
||||
if (open_file (&sun, peer_type, &pipe_name) < 0)
|
||||
{
|
||||
connect_state (unconnected);
|
||||
return -1;
|
||||
}
|
||||
if (peer_type != get_socket_type ())
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
connect_state (unconnected);
|
||||
return -1;
|
||||
}
|
||||
set_peer_sun_path (&sun);
|
||||
if (get_socket_type () != SOCK_DGRAM)
|
||||
{
|
||||
if (connect_pipe (&pipe_name) < 0)
|
||||
{
|
||||
if (get_errno () != EINPROGRESS)
|
||||
{
|
||||
set_peer_sun_path (NULL);
|
||||
connect_state (connect_failed);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
connect_state (connected);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
@ -743,40 +1396,52 @@ fhandler_socket_unix::shutdown (int how)
|
||||
int
|
||||
fhandler_socket_unix::close ()
|
||||
{
|
||||
HANDLE thr = InterlockedExchangePointer (&connect_wait_thr, NULL);
|
||||
if (thr)
|
||||
{
|
||||
TerminateThread (thr, 0);
|
||||
CloseHandle (thr);
|
||||
}
|
||||
PVOID param = InterlockedExchangePointer (&cwt_param, NULL);
|
||||
if (param)
|
||||
cfree (param);
|
||||
if (get_handle ())
|
||||
NtClose (get_handle ());
|
||||
if (file && file != INVALID_HANDLE_VALUE)
|
||||
NtClose (file);
|
||||
if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE)
|
||||
NtClose (backing_file_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_unix::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (get_socket_type () != SOCK_STREAM)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
AcquireSRWLockShared (&conn_lock);
|
||||
if (connect_state () != connected)
|
||||
set_errno (ENOTCONN);
|
||||
else
|
||||
{
|
||||
set_errno (ENOTCONN);
|
||||
return -1;
|
||||
__try
|
||||
{
|
||||
if (pid)
|
||||
*pid = peer_cred.pid;
|
||||
if (euid)
|
||||
*euid = peer_cred.uid;
|
||||
if (egid)
|
||||
*egid = peer_cred.gid;
|
||||
ret = 0;
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
}
|
||||
|
||||
__try
|
||||
{
|
||||
if (pid)
|
||||
*pid = peer_cred.pid;
|
||||
if (euid)
|
||||
*euid = peer_cred.uid;
|
||||
if (egid)
|
||||
*egid = peer_cred.gid;
|
||||
return 0;
|
||||
}
|
||||
__except (EFAULT) {}
|
||||
__endtry
|
||||
return -1;
|
||||
ReleaseSRWLockShared (&conn_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
@ -905,7 +1570,10 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
|
||||
case SO_ERROR:
|
||||
{
|
||||
int *e = (int *) optval;
|
||||
*e = 0;
|
||||
LONG err;
|
||||
|
||||
err = InterlockedExchange (&so_error, 0);
|
||||
*e = err;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1019,6 +1687,15 @@ fhandler_socket_unix::ioctl (unsigned int cmd, void *p)
|
||||
case _IOR('f', 127, int):
|
||||
#endif
|
||||
case FIONBIO:
|
||||
{
|
||||
const bool was_nonblocking = is_nonblocking ();
|
||||
set_nonblocking (*(int *) p);
|
||||
const bool now_nonblocking = is_nonblocking ();
|
||||
if (was_nonblocking != now_nonblocking)
|
||||
set_pipe_non_blocking (now_nonblocking);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
case SIOCATMARK:
|
||||
break;
|
||||
default:
|
||||
@ -1031,7 +1708,7 @@ fhandler_socket_unix::ioctl (unsigned int cmd, void *p)
|
||||
int
|
||||
fhandler_socket_unix::fcntl (int cmd, intptr_t arg)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret = -1;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
@ -1039,6 +1716,20 @@ fhandler_socket_unix::fcntl (int cmd, intptr_t arg)
|
||||
break;
|
||||
case F_GETOWN:
|
||||
break;
|
||||
case F_SETFL:
|
||||
{
|
||||
const bool was_nonblocking = is_nonblocking ();
|
||||
const int allowed_flags = O_APPEND | O_NONBLOCK_MASK;
|
||||
int new_flags = arg & allowed_flags;
|
||||
if ((new_flags & OLD_O_NDELAY) && (new_flags & O_NONBLOCK))
|
||||
new_flags &= ~OLD_O_NDELAY;
|
||||
set_flags ((get_flags () & ~allowed_flags) | new_flags);
|
||||
const bool now_nonblocking = is_nonblocking ();
|
||||
if (was_nonblocking != now_nonblocking)
|
||||
set_pipe_non_blocking (now_nonblocking);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = fhandler_socket::fcntl (cmd, arg);
|
||||
break;
|
||||
|
@ -149,6 +149,8 @@ const int __collate_load_error = 0;
|
||||
extern UNICODE_STRING _RDATA ro_u_natdir = _ROU (L"Directory");
|
||||
extern UNICODE_STRING _RDATA ro_u_natsyml = _ROU (L"SymbolicLink");
|
||||
extern UNICODE_STRING _RDATA ro_u_natdev = _ROU (L"Device");
|
||||
extern UNICODE_STRING _RDATA ro_u_npfs = _ROU (L"\\Device\\NamedPipe");
|
||||
extern UNICODE_STRING _RDATA ro_u_npfs_dir = _ROU (L"\\Device\\NamedPipe\\");
|
||||
#undef _ROU
|
||||
|
||||
/* This is an exported copy of environ which can be used by DLLs
|
||||
|
Loading…
x
Reference in New Issue
Block a user