diff options
Diffstat (limited to 'net/unix')
| -rw-r--r-- | net/unix/af_unix.c | 52 | ||||
| -rw-r--r-- | net/unix/diag.c | 10 | 
2 files changed, 50 insertions, 12 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 85d3bb7490aa..8ee85aa79fa7 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -530,6 +530,16 @@ static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *,  static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *,  				  struct msghdr *, size_t, int); +static void unix_set_peek_off(struct sock *sk, int val) +{ +	struct unix_sock *u = unix_sk(sk); + +	mutex_lock(&u->readlock); +	sk->sk_peek_off = val; +	mutex_unlock(&u->readlock); +} + +  static const struct proto_ops unix_stream_ops = {  	.family =	PF_UNIX,  	.owner =	THIS_MODULE, @@ -549,6 +559,7 @@ static const struct proto_ops unix_stream_ops = {  	.recvmsg =	unix_stream_recvmsg,  	.mmap =		sock_no_mmap,  	.sendpage =	sock_no_sendpage, +	.set_peek_off =	unix_set_peek_off,  };  static const struct proto_ops unix_dgram_ops = { @@ -570,6 +581,7 @@ static const struct proto_ops unix_dgram_ops = {  	.recvmsg =	unix_dgram_recvmsg,  	.mmap =		sock_no_mmap,  	.sendpage =	sock_no_sendpage, +	.set_peek_off =	unix_set_peek_off,  };  static const struct proto_ops unix_seqpacket_ops = { @@ -591,6 +603,7 @@ static const struct proto_ops unix_seqpacket_ops = {  	.recvmsg =	unix_seqpacket_recvmsg,  	.mmap =		sock_no_mmap,  	.sendpage =	sock_no_sendpage, +	.set_peek_off =	unix_set_peek_off,  };  static struct proto unix_proto = { @@ -1756,6 +1769,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,  	int noblock = flags & MSG_DONTWAIT;  	struct sk_buff *skb;  	int err; +	int peeked, skip;  	err = -EOPNOTSUPP;  	if (flags&MSG_OOB) @@ -1769,7 +1783,9 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,  		goto out;  	} -	skb = skb_recv_datagram(sk, flags, noblock, &err); +	skip = sk_peek_offset(sk, flags); + +	skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err);  	if (!skb) {  		unix_state_lock(sk);  		/* Signal EOF on disconnected non-blocking SEQPACKET socket. */ @@ -1786,12 +1802,12 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,  	if (msg->msg_name)  		unix_copy_addr(msg, skb->sk); -	if (size > skb->len) -		size = skb->len; -	else if (size < skb->len) +	if (size > skb->len - skip) +		size = skb->len - skip; +	else if (size < skb->len - skip)  		msg->msg_flags |= MSG_TRUNC; -	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size); +	err = skb_copy_datagram_iovec(skb, skip, msg->msg_iov, size);  	if (err)  		goto out_free; @@ -1808,6 +1824,8 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,  	if (!(flags & MSG_PEEK)) {  		if (UNIXCB(skb).fp)  			unix_detach_fds(siocb->scm, skb); + +		sk_peek_offset_bwd(sk, skb->len);  	} else {  		/* It is questionable: on PEEK we could:  		   - do not return fds - good, but too simple 8) @@ -1821,10 +1839,13 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,  		   clearly however!  		*/ + +		sk_peek_offset_fwd(sk, size); +  		if (UNIXCB(skb).fp)  			siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp);  	} -	err = size; +	err = (flags & MSG_TRUNC) ? skb->len - skip : size;  	scm_recv(sock, msg, siocb->scm, flags); @@ -1884,6 +1905,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  	int target;  	int err = 0;  	long timeo; +	int skip;  	err = -EINVAL;  	if (sk->sk_state != TCP_ESTABLISHED) @@ -1913,12 +1935,15 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  		goto out;  	} +	skip = sk_peek_offset(sk, flags); +  	do {  		int chunk;  		struct sk_buff *skb;  		unix_state_lock(sk);  		skb = skb_peek(&sk->sk_receive_queue); +again:  		if (skb == NULL) {  			unix_sk(sk)->recursion_level = 0;  			if (copied >= target) @@ -1953,6 +1978,13 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  			unix_state_unlock(sk);  			break;  		} + +		if (skip >= skb->len) { +			skip -= skb->len; +			skb = skb_peek_next(skb, &sk->sk_receive_queue); +			goto again; +		} +  		unix_state_unlock(sk);  		if (check_creds) { @@ -1972,8 +2004,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  			sunaddr = NULL;  		} -		chunk = min_t(unsigned int, skb->len, size); -		if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { +		chunk = min_t(unsigned int, skb->len - skip, size); +		if (memcpy_toiovec(msg->msg_iov, skb->data + skip, chunk)) {  			if (copied == 0)  				copied = -EFAULT;  			break; @@ -1985,6 +2017,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  		if (!(flags & MSG_PEEK)) {  			skb_pull(skb, chunk); +			sk_peek_offset_bwd(sk, chunk); +  			if (UNIXCB(skb).fp)  				unix_detach_fds(siocb->scm, skb); @@ -2002,6 +2036,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,  			if (UNIXCB(skb).fp)  				siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp); +			sk_peek_offset_fwd(sk, chunk); +  			break;  		}  	} while (size); diff --git a/net/unix/diag.c b/net/unix/diag.c index 6b7697fd911b..4195555aea65 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -301,10 +301,12 @@ static int unix_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)  	if (nlmsg_len(h) < hdrlen)  		return -EINVAL; -	if (h->nlmsg_flags & NLM_F_DUMP) -		return netlink_dump_start(sock_diag_nlsk, skb, h, -					  unix_diag_dump, NULL, 0); -	else +	if (h->nlmsg_flags & NLM_F_DUMP) { +		struct netlink_dump_control c = { +			.dump = unix_diag_dump, +		}; +		return netlink_dump_start(sock_diag_nlsk, skb, h, &c); +	} else  		return unix_diag_get_exact(skb, h, (struct unix_diag_req *)NLMSG_DATA(h));  }  | 
