diff options
Diffstat (limited to 'net/ipx/af_ipx.c')
-rw-r--r-- | net/ipx/af_ipx.c | 28 |
1 files changed, 26 insertions, 2 deletions
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 00b2a6d1c009..41e4e93cb3aa 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1368,6 +1368,7 @@ static int ipx_release(struct socket *sock) goto out; lock_sock(sk); + sk->sk_shutdown = SHUTDOWN_MASK; if (!sock_flag(sk, SOCK_DEAD)) sk->sk_state_change(sk); @@ -1791,8 +1792,11 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &rc); - if (!skb) + if (!skb) { + if (rc == -EAGAIN && (sk->sk_shutdown & RCV_SHUTDOWN)) + rc = 0; goto out; + } ipx = ipx_hdr(skb); copied = ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr); @@ -1922,6 +1926,26 @@ static int ipx_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long } #endif +static int ipx_shutdown(struct socket *sock, int mode) +{ + struct sock *sk = sock->sk; + + if (mode < SHUT_RD || mode > SHUT_RDWR) + return -EINVAL; + /* This maps: + * SHUT_RD (0) -> RCV_SHUTDOWN (1) + * SHUT_WR (1) -> SEND_SHUTDOWN (2) + * SHUT_RDWR (2) -> SHUTDOWN_MASK (3) + */ + ++mode; + + lock_sock(sk); + sk->sk_shutdown |= mode; + release_sock(sk); + sk->sk_state_change(sk); + + return 0; +} /* * Socket family declarations @@ -1948,7 +1972,7 @@ static const struct proto_ops ipx_dgram_ops = { .compat_ioctl = ipx_compat_ioctl, #endif .listen = sock_no_listen, - .shutdown = sock_no_shutdown, /* FIXME: support shutdown */ + .shutdown = ipx_shutdown, .setsockopt = ipx_setsockopt, .getsockopt = ipx_getsockopt, .sendmsg = ipx_sendmsg, |