Cygwin: AF_UNIX: implement recvmsg
First cut. Not yet tested.
This commit is contained in:
parent
f95148f156
commit
9037f01e51
|
@ -1102,6 +1102,8 @@ class fhandler_socket_unix : public fhandler_socket
|
||||||
void set_pipe_non_blocking (bool nonblocking);
|
void set_pipe_non_blocking (bool nonblocking);
|
||||||
int send_sock_info (bool from_bind);
|
int send_sock_info (bool from_bind);
|
||||||
void xchg_sock_info ();
|
void xchg_sock_info ();
|
||||||
|
void record_shut_info (af_unix_pkt_hdr_t *packet);
|
||||||
|
void process_admin_pkt (af_unix_pkt_hdr_t *packet);
|
||||||
int grab_admin_pkt (bool peek = true);
|
int grab_admin_pkt (bool peek = true);
|
||||||
int recv_peer_info ();
|
int recv_peer_info ();
|
||||||
static NTSTATUS npfs_handle (HANDLE &nph);
|
static NTSTATUS npfs_handle (HANDLE &nph);
|
||||||
|
@ -1164,6 +1166,9 @@ class fhandler_socket_unix : public fhandler_socket
|
||||||
int open (int flags, mode_t mode = 0);
|
int open (int flags, mode_t mode = 0);
|
||||||
int close ();
|
int close ();
|
||||||
int getpeereid (pid_t *pid, uid_t *euid, gid_t *egid);
|
int getpeereid (pid_t *pid, uid_t *euid, gid_t *egid);
|
||||||
|
bool evaluate_cmsg_data (af_unix_pkt_hdr_t *packet, struct msghdr *msg,
|
||||||
|
bool cloexec = false);
|
||||||
|
int handle_partial_read (af_unix_pkt_hdr_t *packet, size_t excess);
|
||||||
ssize_t recvmsg (struct msghdr *msg, int flags);
|
ssize_t recvmsg (struct msghdr *msg, int flags);
|
||||||
ssize_t recvfrom (void *ptr, size_t len, int flags,
|
ssize_t recvfrom (void *ptr, size_t len, int flags,
|
||||||
struct sockaddr *from, int *fromlen);
|
struct sockaddr *from, int *fromlen);
|
||||||
|
|
|
@ -700,6 +700,44 @@ fhandler_socket_unix::xchg_sock_info ()
|
||||||
recv_peer_info ();
|
recv_peer_info ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fhandler_socket_unix::record_shut_info (af_unix_pkt_hdr_t *packet)
|
||||||
|
{
|
||||||
|
state_lock ();
|
||||||
|
if (packet->shut_info)
|
||||||
|
{
|
||||||
|
/* Peer's shutdown sends the SHUT flags as used by the peer.
|
||||||
|
They have to be reversed for our side. */
|
||||||
|
int shut_info = saw_shutdown ();
|
||||||
|
if (packet->shut_info & _SHUT_RECV)
|
||||||
|
shut_info |= _SHUT_SEND;
|
||||||
|
if (packet->shut_info & _SHUT_SEND)
|
||||||
|
shut_info |= _SHUT_RECV;
|
||||||
|
saw_shutdown (shut_info);
|
||||||
|
/* FIXME: anything else here? */
|
||||||
|
}
|
||||||
|
state_unlock ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fhandler_socket_unix::process_admin_pkt (af_unix_pkt_hdr_t *packet)
|
||||||
|
{
|
||||||
|
record_shut_info (packet);
|
||||||
|
state_lock ();
|
||||||
|
if (packet->name_len > 0)
|
||||||
|
peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len);
|
||||||
|
if (packet->cmsg_len > 0)
|
||||||
|
{
|
||||||
|
struct cmsghdr *cmsg = (struct cmsghdr *)
|
||||||
|
alloca (packet->cmsg_len);
|
||||||
|
memcpy (cmsg, AF_UNIX_PKT_CMSG (packet), packet->cmsg_len);
|
||||||
|
if (cmsg->cmsg_level == SOL_SOCKET
|
||||||
|
&& cmsg->cmsg_type == SCM_CREDENTIALS)
|
||||||
|
peer_cred ((struct ucred *) CMSG_DATA(cmsg));
|
||||||
|
}
|
||||||
|
state_unlock ();
|
||||||
|
}
|
||||||
|
|
||||||
/* Reads an administrative packet from the pipe and handles it. If
|
/* Reads an administrative packet from the pipe and handles it. If
|
||||||
PEEK is true, checks first to see if the next packet in the pipe is
|
PEEK is true, checks first to see if the next packet in the pipe is
|
||||||
an administrative packet; otherwise the caller must set io_lock and
|
an administrative packet; otherwise the caller must set io_lock and
|
||||||
|
@ -732,31 +770,7 @@ fhandler_socket_unix::grab_admin_pkt (bool peek)
|
||||||
io_unlock ();
|
io_unlock ();
|
||||||
if (nr < 0)
|
if (nr < 0)
|
||||||
return get_errno ();
|
return get_errno ();
|
||||||
state_lock ();
|
process_admin_pkt (packet);
|
||||||
if (packet->shut_info)
|
|
||||||
{
|
|
||||||
/* Peer's shutdown sends the SHUT flags as used by the peer.
|
|
||||||
They have to be reversed for our side. */
|
|
||||||
int shut_info = saw_shutdown ();
|
|
||||||
if (packet->shut_info & _SHUT_RECV)
|
|
||||||
shut_info |= _SHUT_SEND;
|
|
||||||
if (packet->shut_info & _SHUT_SEND)
|
|
||||||
shut_info |= _SHUT_RECV;
|
|
||||||
saw_shutdown (shut_info);
|
|
||||||
/* FIXME: anything else here? */
|
|
||||||
}
|
|
||||||
if (packet->name_len > 0)
|
|
||||||
peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len);
|
|
||||||
if (packet->cmsg_len > 0)
|
|
||||||
{
|
|
||||||
struct cmsghdr *cmsg = (struct cmsghdr *)
|
|
||||||
alloca (packet->cmsg_len);
|
|
||||||
memcpy (cmsg, AF_UNIX_PKT_CMSG (packet), packet->cmsg_len);
|
|
||||||
if (cmsg->cmsg_level == SOL_SOCKET
|
|
||||||
&& cmsg->cmsg_type == SCM_CREDENTIALS)
|
|
||||||
peer_cred ((struct ucred *) CMSG_DATA(cmsg));
|
|
||||||
}
|
|
||||||
state_unlock ();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1803,11 +1817,297 @@ fhandler_socket_unix::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
fhandler_socket_unix::evaluate_cmsg_data (af_unix_pkt_hdr_t *packet,
|
||||||
|
struct msghdr *msg, bool cloexec)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
fhandler_socket_unix::handle_partial_read (af_unix_pkt_hdr_t *packet,
|
||||||
|
size_t excess)
|
||||||
|
{
|
||||||
|
tmp_pathbuf tp;
|
||||||
|
af_unix_pkt_hdr_t *new_pkt = (af_unix_pkt_hdr_t *) tp.w_get ();
|
||||||
|
|
||||||
|
new_pkt->init (false, _SHUT_NONE, 0, 0, excess);
|
||||||
|
memcpy (AF_UNIX_PKT_DATA (new_pkt),
|
||||||
|
(char *) AF_UNIX_PKT_DATA_END (packet) - excess, excess);
|
||||||
|
return mq_send (get_mqd_in (), (const char *) new_pkt, new_pkt->pckt_len,
|
||||||
|
af_un_prio_rewrite);
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
fhandler_socket_unix::recvmsg (struct msghdr *msg, int flags)
|
fhandler_socket_unix::recvmsg (struct msghdr *msg, int flags)
|
||||||
{
|
{
|
||||||
set_errno (EAFNOSUPPORT);
|
size_t nbytes_read = 0;
|
||||||
return -1;
|
ssize_t ret = -1;
|
||||||
|
size_t tot;
|
||||||
|
bool waitall = false;
|
||||||
|
bool name_read = false;
|
||||||
|
bool shutdown = saw_shutdown_read ();
|
||||||
|
int err;
|
||||||
|
tmp_pathbuf tp;
|
||||||
|
af_unix_pkt_hdr_t *packet = (af_unix_pkt_hdr_t *) tp.w_get ();
|
||||||
|
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
/* Valid flags: MSG_DONTWAIT, MSG_PEEK, MSG_WAITALL, MSG_TRUNC. */
|
||||||
|
if (flags & ~(MSG_DONTWAIT | MSG_PEEK | MSG_WAITALL | MSG_TRUNC))
|
||||||
|
{
|
||||||
|
set_errno (EOPNOTSUPP);
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
/* FIXME: Check this. It's from Stevens, UNIX Network
|
||||||
|
Programming, discussion of select. He doesn't say whether we
|
||||||
|
should also clear so_error. */
|
||||||
|
err = so_error ();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
set_errno (err);
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make local copy of scatter-gather array and calculate number
|
||||||
|
of bytes to be read. */
|
||||||
|
size_t my_iovlen = msg->msg_iovlen;
|
||||||
|
struct iovec my_iov[my_iovlen];
|
||||||
|
struct iovec *my_iovptr = my_iov + my_iovlen;
|
||||||
|
const struct iovec *iovptr = msg->msg_iov + msg->msg_iovlen;
|
||||||
|
tot = 0;
|
||||||
|
while (--my_iovptr >= my_iov)
|
||||||
|
{
|
||||||
|
*my_iovptr = *(--iovptr);
|
||||||
|
tot += iovptr->iov_len;
|
||||||
|
}
|
||||||
|
if (!tot)
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
if (get_socket_type () == SOCK_STREAM)
|
||||||
|
{
|
||||||
|
if (connect_state () != connected)
|
||||||
|
{
|
||||||
|
set_errno (ENOTCONN);
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
if ((flags & MSG_WAITALL) && !(flags & (MSG_PEEK | MSG_DONTWAIT))
|
||||||
|
&& !is_nonblocking ())
|
||||||
|
waitall = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (connect_state () == connected)
|
||||||
|
{
|
||||||
|
/* FIXME: Make sure sender really is peer? */
|
||||||
|
;
|
||||||
|
}
|
||||||
|
if (bind_state () != bound)
|
||||||
|
{
|
||||||
|
if (is_nonblocking () || (flags & MSG_DONTWAIT))
|
||||||
|
{
|
||||||
|
set_errno (EAGAIN);
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* FIXME: Linux blocks, but I'm not sure what to
|
||||||
|
wait for. Do we need to poll to see if we become
|
||||||
|
bound? */
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & MSG_PEEK)
|
||||||
|
{
|
||||||
|
bool nonblocking = is_nonblocking () || (flags & MSG_DONTWAIT);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
ssize_t nr;
|
||||||
|
|
||||||
|
io_lock ();
|
||||||
|
if (shutdown)
|
||||||
|
{
|
||||||
|
nr = peek_mqueue ((char *) packet, MAX_AF_UN_PKT_LEN, true);
|
||||||
|
if (nr <= 0)
|
||||||
|
{
|
||||||
|
io_unlock ();
|
||||||
|
ret = 0; /* EOF */
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nr = peek_mqueue ((char *) packet, MAX_AF_UN_PKT_LEN,
|
||||||
|
nonblocking);
|
||||||
|
if (nr < 0)
|
||||||
|
{
|
||||||
|
io_unlock ();
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
if (packet->admin_pkt)
|
||||||
|
{
|
||||||
|
grab_admin_pkt (false); /* Will call io_unlock. */
|
||||||
|
shutdown = saw_shutdown_read ();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
io_unlock ();
|
||||||
|
record_shut_info (packet);
|
||||||
|
ret = packet->data_len;
|
||||||
|
size_t nbytes = ret; /* Number of bytes to copy to buffers. */
|
||||||
|
if (tot < nbytes)
|
||||||
|
{
|
||||||
|
nbytes = tot;
|
||||||
|
if (get_socket_type () == SOCK_STREAM)
|
||||||
|
ret = tot;
|
||||||
|
}
|
||||||
|
char *ptr = (char *) AF_UNIX_PKT_DATA (packet);
|
||||||
|
for (struct iovec *iovptr = msg->msg_iov; nbytes > 0; ++iovptr)
|
||||||
|
{
|
||||||
|
size_t frag = MIN (nbytes, iovptr->iov_len);
|
||||||
|
memcpy (iovptr->iov_base, ptr, frag);
|
||||||
|
ptr += frag;
|
||||||
|
nbytes -= frag;
|
||||||
|
}
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MSG_PEEK is not set. Normal read. */
|
||||||
|
my_iovptr = my_iov;
|
||||||
|
msg->msg_flags = 0;
|
||||||
|
while (tot)
|
||||||
|
{
|
||||||
|
size_t nbytes_now = 0;
|
||||||
|
size_t excess = 0;
|
||||||
|
ssize_t nr;
|
||||||
|
|
||||||
|
if (shutdown)
|
||||||
|
{
|
||||||
|
if (peek_mqueue ((char *) packet, MAX_AF_UN_PKT_LEN, true) < 0)
|
||||||
|
{
|
||||||
|
ret = nbytes_read;
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io_lock ();
|
||||||
|
/* Handle MSG_DONTWAIT in blocking mode. */
|
||||||
|
if (!is_nonblocking () && (flags & MSG_DONTWAIT))
|
||||||
|
set_mqueue_non_blocking (get_mqd_in (), true);
|
||||||
|
nr = _mq_recv (get_mqd_in (), (char *) packet,
|
||||||
|
MAX_AF_UN_PKT_LEN, _MQ_HOLD_LOCK);
|
||||||
|
if (!is_nonblocking () && (flags & MSG_DONTWAIT))
|
||||||
|
set_mqueue_non_blocking (get_mqd_in (), false);
|
||||||
|
io_unlock ();
|
||||||
|
/* FIXME: Check for error in _mq_unlock calls below? */
|
||||||
|
if (nr < 0)
|
||||||
|
{
|
||||||
|
_mq_unlock (get_mqd_in ());
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
if (packet->admin_pkt)
|
||||||
|
{
|
||||||
|
_mq_unlock (get_mqd_in ());
|
||||||
|
process_admin_pkt (packet);
|
||||||
|
shutdown = saw_shutdown_read ();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
record_shut_info (packet);
|
||||||
|
shutdown = saw_shutdown_read ();
|
||||||
|
nbytes_now = packet->data_len;
|
||||||
|
nbytes_read += nbytes_now;
|
||||||
|
if (nbytes_now > tot)
|
||||||
|
excess = nbytes_now - tot;
|
||||||
|
if (excess)
|
||||||
|
{
|
||||||
|
nbytes_now = tot;
|
||||||
|
if (get_socket_type () == SOCK_STREAM)
|
||||||
|
{
|
||||||
|
nbytes_read -= excess;
|
||||||
|
/* Put excess bytes back on the message queue. */
|
||||||
|
if (handle_partial_read (packet, excess) < 0)
|
||||||
|
{
|
||||||
|
_mq_unlock (get_mqd_in ());
|
||||||
|
debug_printf ("Couldn't handle partial read, %E");
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Truncate the data to what was requested. */
|
||||||
|
if (!(flags & MSG_TRUNC))
|
||||||
|
nbytes_read = tot;
|
||||||
|
msg->msg_flags |= MSG_TRUNC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_mq_unlock (get_mqd_in ());
|
||||||
|
if (msg->msg_name && !name_read)
|
||||||
|
{
|
||||||
|
sun_name_t sun ((struct sockaddr *) AF_UNIX_PKT_NAME (packet),
|
||||||
|
packet->name_len);
|
||||||
|
memcpy (msg->msg_name, &sun.un,
|
||||||
|
MIN (msg->msg_namelen, sun.un_len + 1));
|
||||||
|
msg->msg_namelen = sun.un_len;
|
||||||
|
name_read = true;
|
||||||
|
}
|
||||||
|
if (msg->msg_controllen)
|
||||||
|
{
|
||||||
|
if (!evaluate_cmsg_data (packet, msg))
|
||||||
|
__leave;
|
||||||
|
/* https://man7.org/linux/man-pages/man7/unix.7.html
|
||||||
|
says that ancillary data is a barrier to further
|
||||||
|
reading. */
|
||||||
|
waitall = false;
|
||||||
|
}
|
||||||
|
if (!nbytes_now)
|
||||||
|
{
|
||||||
|
/* 0-length datagrams are allowed. */
|
||||||
|
if (get_socket_type () == SOCK_DGRAM)
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_errno (EIO);
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Copy data to scatter-gather buffers. */
|
||||||
|
char *ptr = (char *) AF_UNIX_PKT_DATA (packet);
|
||||||
|
while (nbytes_now && my_iovlen)
|
||||||
|
{
|
||||||
|
if (my_iovptr->iov_len > nbytes_now)
|
||||||
|
{
|
||||||
|
memcpy (my_iovptr->iov_base, ptr, nbytes_now);
|
||||||
|
my_iovptr->iov_base = (char *) my_iovptr->iov_base
|
||||||
|
+ nbytes_now;
|
||||||
|
my_iovptr->iov_len -= nbytes_now;
|
||||||
|
nbytes_now = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy (my_iovptr->iov_base, ptr, my_iovptr->iov_len);
|
||||||
|
ptr += my_iovptr->iov_len;
|
||||||
|
nbytes_now -= my_iovptr->iov_len;
|
||||||
|
++my_iovptr;
|
||||||
|
--my_iovlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(waitall && my_iovlen))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (nbytes_read)
|
||||||
|
ret = nbytes_read;
|
||||||
|
}
|
||||||
|
__except (EFAULT)
|
||||||
|
__endtry
|
||||||
|
if (msg->msg_name && !name_read)
|
||||||
|
msg->msg_namelen = 0;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
|
@ -1836,8 +2136,6 @@ fhandler_socket_unix::recvfrom (void *ptr, size_t len, int flags,
|
||||||
void __reg3
|
void __reg3
|
||||||
fhandler_socket_unix::read (void *ptr, size_t& len)
|
fhandler_socket_unix::read (void *ptr, size_t& len)
|
||||||
{
|
{
|
||||||
set_errno (EAFNOSUPPORT);
|
|
||||||
len = 0;
|
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
|
|
||||||
|
@ -1900,7 +2198,7 @@ fhandler_socket_unix::sendmsg (const struct msghdr *msg, int flags)
|
||||||
}
|
}
|
||||||
/* FIXME: Check this. It's from Stevens, UNIX Network
|
/* FIXME: Check this. It's from Stevens, UNIX Network
|
||||||
Programming, discussion of select. He doesn't say whether we
|
Programming, discussion of select. He doesn't say whether we
|
||||||
also clear so_error.*/
|
should also clear so_error.*/
|
||||||
err = so_error ();
|
err = so_error ();
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue