aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen Brown <kbrown@cornell.edu>2021-05-13 18:21:53 -0400
committerKen Brown <kbrown@cornell.edu>2021-06-04 12:41:43 -0400
commit9037f01e51ab45e3b0b04b8e834a02d3ff143b55 (patch)
tree4bdc29939fc45612abd4e8ac2c22705af505cd72
parentf95148f1560eb4e4e9bd2a3b9be1722589e4baa2 (diff)
downloadnewlib-topic/af_unix_mq.zip
newlib-topic/af_unix_mq.tar.gz
newlib-topic/af_unix_mq.tar.bz2
Cygwin: AF_UNIX: implement recvmsggithub/topic/af_unix_mqtopic/af_unix_mq
First cut. Not yet tested.
-rw-r--r--winsup/cygwin/fhandler.h5
-rw-r--r--winsup/cygwin/fhandler_socket_unix.cc358
2 files changed, 333 insertions, 30 deletions
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 109e73d..a361786 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1102,6 +1102,8 @@ class fhandler_socket_unix : public fhandler_socket
void set_pipe_non_blocking (bool nonblocking);
int send_sock_info (bool from_bind);
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 recv_peer_info ();
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 close ();
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 recvfrom (void *ptr, size_t len, int flags,
struct sockaddr *from, int *fromlen);
diff --git a/winsup/cygwin/fhandler_socket_unix.cc b/winsup/cygwin/fhandler_socket_unix.cc
index 825bf1c..00109ae 100644
--- a/winsup/cygwin/fhandler_socket_unix.cc
+++ b/winsup/cygwin/fhandler_socket_unix.cc
@@ -700,6 +700,44 @@ fhandler_socket_unix::xchg_sock_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
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
@@ -732,31 +770,7 @@ fhandler_socket_unix::grab_admin_pkt (bool peek)
io_unlock ();
if (nr < 0)
return get_errno ();
- 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? */
- }
- 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 ();
+ process_admin_pkt (packet);
return 0;
}
@@ -1803,11 +1817,297 @@ fhandler_socket_unix::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid)
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
fhandler_socket_unix::recvmsg (struct msghdr *msg, int flags)
{
- set_errno (EAFNOSUPPORT);
- return -1;
+ size_t nbytes_read = 0;
+ 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
@@ -1836,8 +2136,6 @@ fhandler_socket_unix::recvfrom (void *ptr, size_t len, int flags,
void __reg3
fhandler_socket_unix::read (void *ptr, size_t& len)
{
- set_errno (EAFNOSUPPORT);
- len = 0;
struct iovec iov;
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
Programming, discussion of select. He doesn't say whether we
- also clear so_error.*/
+ should also clear so_error.*/
err = so_error ();
if (err)
{