diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 110 | 
1 files changed, 65 insertions, 45 deletions
| diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index aad8fb699989..d510353ef431 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -293,7 +293,7 @@ static struct sock *unix_find_socket_byinode(struct inode *i)  	spin_lock(&unix_table_lock);  	sk_for_each(s, node,  		    &unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) { -		struct dentry *dentry = unix_sk(s)->dentry; +		struct dentry *dentry = unix_sk(s)->path.dentry;  		if (dentry && dentry->d_inode == i) {  			sock_hold(s); @@ -377,8 +377,7 @@ static void unix_sock_destructor(struct sock *sk)  static int unix_release_sock(struct sock *sk, int embrion)  {  	struct unix_sock *u = unix_sk(sk); -	struct dentry *dentry; -	struct vfsmount *mnt; +	struct path path;  	struct sock *skpair;  	struct sk_buff *skb;  	int state; @@ -389,10 +388,9 @@ static int unix_release_sock(struct sock *sk, int embrion)  	unix_state_lock(sk);  	sock_orphan(sk);  	sk->sk_shutdown = SHUTDOWN_MASK; -	dentry	     = u->dentry; -	u->dentry    = NULL; -	mnt	     = u->mnt; -	u->mnt	     = NULL; +	path	     = u->path; +	u->path.dentry = NULL; +	u->path.mnt = NULL;  	state = sk->sk_state;  	sk->sk_state = TCP_CLOSE;  	unix_state_unlock(sk); @@ -425,10 +423,8 @@ static int unix_release_sock(struct sock *sk, int embrion)  		kfree_skb(skb);  	} -	if (dentry) { -		dput(dentry); -		mntput(mnt); -	} +	if (path.dentry) +		path_put(&path);  	sock_put(sk); @@ -530,6 +526,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 +555,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 +577,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 +599,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 = { @@ -628,8 +637,8 @@ static struct sock *unix_create1(struct net *net, struct socket *sock)  	sk->sk_max_ack_backlog	= net->unx.sysctl_max_dgram_qlen;  	sk->sk_destruct		= unix_sock_destructor;  	u	  = unix_sk(sk); -	u->dentry = NULL; -	u->mnt	  = NULL; +	u->path.dentry = NULL; +	u->path.mnt = NULL;  	spin_lock_init(&u->lock);  	atomic_long_set(&u->inflight, 0);  	INIT_LIST_HEAD(&u->link); @@ -775,7 +784,7 @@ static struct sock *unix_find_other(struct net *net,  			goto put_fail;  		if (u->sk_type == type) -			touch_atime(path.mnt, path.dentry); +			touch_atime(&path);  		path_put(&path); @@ -789,9 +798,9 @@ static struct sock *unix_find_other(struct net *net,  		u = unix_find_socket_byname(net, sunname, len, type, hash);  		if (u) {  			struct dentry *dentry; -			dentry = unix_sk(u)->dentry; +			dentry = unix_sk(u)->path.dentry;  			if (dentry) -				touch_atime(unix_sk(u)->mnt, dentry); +				touch_atime(&unix_sk(u)->path);  		} else  			goto fail;  	} @@ -897,8 +906,7 @@ out_mknod_drop_write:  		list = &unix_socket_table[addr->hash];  	} else {  		list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)]; -		u->dentry = path.dentry; -		u->mnt    = path.mnt; +		u->path = path;  	}  	err = 0; @@ -1180,9 +1188,9 @@ restart:  		atomic_inc(&otheru->addr->refcnt);  		newu->addr = otheru->addr;  	} -	if (otheru->dentry) { -		newu->dentry	= dget(otheru->dentry); -		newu->mnt	= mntget(otheru->mnt); +	if (otheru->path.dentry) { +		path_get(&otheru->path); +		newu->path = otheru->path;  	}  	/* Set credentials */ @@ -1756,6 +1764,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 +1778,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 +1797,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 +1819,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 +1834,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 +1900,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 +1930,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_dequeue(&sk->sk_receive_queue); +		skb = skb_peek(&sk->sk_receive_queue); +again:  		if (skb == NULL) {  			unix_sk(sk)->recursion_level = 0;  			if (copied >= target) @@ -1953,16 +1973,20 @@ 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) {  			/* Never glue messages from different writers */  			if ((UNIXCB(skb).pid  != siocb->scm->pid) || -			    (UNIXCB(skb).cred != siocb->scm->cred)) { -				skb_queue_head(&sk->sk_receive_queue, skb); -				sk->sk_data_ready(sk, skb->len); +			    (UNIXCB(skb).cred != siocb->scm->cred))  				break; -			}  		} else {  			/* Copy credentials */  			scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred); @@ -1975,10 +1999,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)) { -			skb_queue_head(&sk->sk_receive_queue, skb); -			sk->sk_data_ready(sk, skb->len); +		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; @@ -1990,16 +2012,15 @@ 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); -			/* put the skb back if we didn't use it up.. */ -			if (skb->len) { -				skb_queue_head(&sk->sk_receive_queue, skb); -				sk->sk_data_ready(sk, skb->len); +			if (skb->len)  				break; -			} +			skb_unlink(skb, &sk->sk_receive_queue);  			consume_skb(skb);  			if (siocb->scm->fp) @@ -2010,9 +2031,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); -			/* put message back and return */ -			skb_queue_head(&sk->sk_receive_queue, skb); -			sk->sk_data_ready(sk, skb->len); +			sk_peek_offset_fwd(sk, chunk); +  			break;  		}  	} while (size); @@ -2186,7 +2206,7 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,  	}  	/* No write status requested, avoid expensive OUT tests. */ -	if (wait && !(wait->key & (POLLWRBAND | POLLWRNORM | POLLOUT))) +	if (!(poll_requested_events(wait) & (POLLWRBAND|POLLWRNORM|POLLOUT)))  		return mask;  	writable = unix_writable(sk); | 
