Cygwin: AF_UNIX: implement recvmsg

First cut.  Not yet tested.
This commit is contained in:
Ken Brown 2021-05-13 18:21:53 -04:00
parent f95148f156
commit 9037f01e51
2 changed files with 333 additions and 30 deletions

View File

@ -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);

View File

@ -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)
{ {