⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 af_unix.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
};static const struct proto_ops unix_dgram_ops = {	.family =	PF_UNIX,	.owner =	THIS_MODULE,	.release =	unix_release,	.bind =		unix_bind,	.connect =	unix_dgram_connect,	.socketpair =	unix_socketpair,	.accept =	sock_no_accept,	.getname =	unix_getname,	.poll =		datagram_poll,	.ioctl =	unix_ioctl,	.listen =	sock_no_listen,	.shutdown =	unix_shutdown,	.setsockopt =	sock_no_setsockopt,	.getsockopt =	sock_no_getsockopt,	.sendmsg =	unix_dgram_sendmsg,	.recvmsg =	unix_dgram_recvmsg,	.mmap =		sock_no_mmap,	.sendpage =	sock_no_sendpage,};static const struct proto_ops unix_seqpacket_ops = {	.family =	PF_UNIX,	.owner =	THIS_MODULE,	.release =	unix_release,	.bind =		unix_bind,	.connect =	unix_stream_connect,	.socketpair =	unix_socketpair,	.accept =	unix_accept,	.getname =	unix_getname,	.poll =		datagram_poll,	.ioctl =	unix_ioctl,	.listen =	unix_listen,	.shutdown =	unix_shutdown,	.setsockopt =	sock_no_setsockopt,	.getsockopt =	sock_no_getsockopt,	.sendmsg =	unix_seqpacket_sendmsg,	.recvmsg =	unix_dgram_recvmsg,	.mmap =		sock_no_mmap,	.sendpage =	sock_no_sendpage,};static struct proto unix_proto = {	.name	  = "UNIX",	.owner	  = THIS_MODULE,	.obj_size = sizeof(struct unix_sock),};/* * AF_UNIX sockets do not interact with hardware, hence they * dont trigger interrupts - so it's safe for them to have * bh-unsafe locking for their sk_receive_queue.lock. Split off * this special lock-class by reinitializing the spinlock key: */static struct lock_class_key af_unix_sk_receive_queue_lock_key;static struct sock * unix_create1(struct net *net, struct socket *sock){	struct sock *sk = NULL;	struct unix_sock *u;	atomic_inc(&unix_nr_socks);	if (atomic_read(&unix_nr_socks) > 2 * get_max_files())		goto out;	sk = sk_alloc(net, PF_UNIX, GFP_KERNEL, &unix_proto);	if (!sk)		goto out;	sock_init_data(sock,sk);	lockdep_set_class(&sk->sk_receive_queue.lock,				&af_unix_sk_receive_queue_lock_key);	sk->sk_write_space	= unix_write_space;	sk->sk_max_ack_backlog	= sysctl_unix_max_dgram_qlen;	sk->sk_destruct		= unix_sock_destructor;	u	  = unix_sk(sk);	u->dentry = NULL;	u->mnt	  = NULL;	spin_lock_init(&u->lock);	atomic_set(&u->inflight, 0);	INIT_LIST_HEAD(&u->link);	mutex_init(&u->readlock); /* single task reading lock */	init_waitqueue_head(&u->peer_wait);	unix_insert_socket(unix_sockets_unbound, sk);out:	if (sk == NULL)		atomic_dec(&unix_nr_socks);	return sk;}static int unix_create(struct net *net, struct socket *sock, int protocol){	if (net != &init_net)		return -EAFNOSUPPORT;	if (protocol && protocol != PF_UNIX)		return -EPROTONOSUPPORT;	sock->state = SS_UNCONNECTED;	switch (sock->type) {	case SOCK_STREAM:		sock->ops = &unix_stream_ops;		break;		/*		 *	Believe it or not BSD has AF_UNIX, SOCK_RAW though		 *	nothing uses it.		 */	case SOCK_RAW:		sock->type=SOCK_DGRAM;	case SOCK_DGRAM:		sock->ops = &unix_dgram_ops;		break;	case SOCK_SEQPACKET:		sock->ops = &unix_seqpacket_ops;		break;	default:		return -ESOCKTNOSUPPORT;	}	return unix_create1(net, sock) ? 0 : -ENOMEM;}static int unix_release(struct socket *sock){	struct sock *sk = sock->sk;	if (!sk)		return 0;	sock->sk = NULL;	return unix_release_sock (sk, 0);}static int unix_autobind(struct socket *sock){	struct sock *sk = sock->sk;	struct unix_sock *u = unix_sk(sk);	static u32 ordernum = 1;	struct unix_address * addr;	int err;	mutex_lock(&u->readlock);	err = 0;	if (u->addr)		goto out;	err = -ENOMEM;	addr = kzalloc(sizeof(*addr) + sizeof(short) + 16, GFP_KERNEL);	if (!addr)		goto out;	addr->name->sun_family = AF_UNIX;	atomic_set(&addr->refcnt, 1);retry:	addr->len = sprintf(addr->name->sun_path+1, "%05x", ordernum) + 1 + sizeof(short);	addr->hash = unix_hash_fold(csum_partial((void*)addr->name, addr->len, 0));	spin_lock(&unix_table_lock);	ordernum = (ordernum+1)&0xFFFFF;	if (__unix_find_socket_byname(addr->name, addr->len, sock->type,				      addr->hash)) {		spin_unlock(&unix_table_lock);		/* Sanity yield. It is unusual case, but yet... */		if (!(ordernum&0xFF))			yield();		goto retry;	}	addr->hash ^= sk->sk_type;	__unix_remove_socket(sk);	u->addr = addr;	__unix_insert_socket(&unix_socket_table[addr->hash], sk);	spin_unlock(&unix_table_lock);	err = 0;out:	mutex_unlock(&u->readlock);	return err;}static struct sock *unix_find_other(struct sockaddr_un *sunname, int len,				    int type, unsigned hash, int *error){	struct sock *u;	struct nameidata nd;	int err = 0;	if (sunname->sun_path[0]) {		err = path_lookup(sunname->sun_path, LOOKUP_FOLLOW, &nd);		if (err)			goto fail;		err = vfs_permission(&nd, MAY_WRITE);		if (err)			goto put_fail;		err = -ECONNREFUSED;		if (!S_ISSOCK(nd.dentry->d_inode->i_mode))			goto put_fail;		u=unix_find_socket_byinode(nd.dentry->d_inode);		if (!u)			goto put_fail;		if (u->sk_type == type)			touch_atime(nd.mnt, nd.dentry);		path_release(&nd);		err=-EPROTOTYPE;		if (u->sk_type != type) {			sock_put(u);			goto fail;		}	} else {		err = -ECONNREFUSED;		u=unix_find_socket_byname(sunname, len, type, hash);		if (u) {			struct dentry *dentry;			dentry = unix_sk(u)->dentry;			if (dentry)				touch_atime(unix_sk(u)->mnt, dentry);		} else			goto fail;	}	return u;put_fail:	path_release(&nd);fail:	*error=err;	return NULL;}static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len){	struct sock *sk = sock->sk;	struct unix_sock *u = unix_sk(sk);	struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;	struct dentry * dentry = NULL;	struct nameidata nd;	int err;	unsigned hash;	struct unix_address *addr;	struct hlist_head *list;	err = -EINVAL;	if (sunaddr->sun_family != AF_UNIX)		goto out;	if (addr_len==sizeof(short)) {		err = unix_autobind(sock);		goto out;	}	err = unix_mkname(sunaddr, addr_len, &hash);	if (err < 0)		goto out;	addr_len = err;	mutex_lock(&u->readlock);	err = -EINVAL;	if (u->addr)		goto out_up;	err = -ENOMEM;	addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);	if (!addr)		goto out_up;	memcpy(addr->name, sunaddr, addr_len);	addr->len = addr_len;	addr->hash = hash ^ sk->sk_type;	atomic_set(&addr->refcnt, 1);	if (sunaddr->sun_path[0]) {		unsigned int mode;		err = 0;		/*		 * Get the parent directory, calculate the hash for last		 * component.		 */		err = path_lookup(sunaddr->sun_path, LOOKUP_PARENT, &nd);		if (err)			goto out_mknod_parent;		dentry = lookup_create(&nd, 0);		err = PTR_ERR(dentry);		if (IS_ERR(dentry))			goto out_mknod_unlock;		/*		 * All right, let's create it.		 */		mode = S_IFSOCK |		       (SOCK_INODE(sock)->i_mode & ~current->fs->umask);		err = vfs_mknod(nd.dentry->d_inode, dentry, mode, 0);		if (err)			goto out_mknod_dput;		mutex_unlock(&nd.dentry->d_inode->i_mutex);		dput(nd.dentry);		nd.dentry = dentry;		addr->hash = UNIX_HASH_SIZE;	}	spin_lock(&unix_table_lock);	if (!sunaddr->sun_path[0]) {		err = -EADDRINUSE;		if (__unix_find_socket_byname(sunaddr, addr_len,					      sk->sk_type, hash)) {			unix_release_addr(addr);			goto out_unlock;		}		list = &unix_socket_table[addr->hash];	} else {		list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)];		u->dentry = nd.dentry;		u->mnt    = nd.mnt;	}	err = 0;	__unix_remove_socket(sk);	u->addr = addr;	__unix_insert_socket(list, sk);out_unlock:	spin_unlock(&unix_table_lock);out_up:	mutex_unlock(&u->readlock);out:	return err;out_mknod_dput:	dput(dentry);out_mknod_unlock:	mutex_unlock(&nd.dentry->d_inode->i_mutex);	path_release(&nd);out_mknod_parent:	if (err==-EEXIST)		err=-EADDRINUSE;	unix_release_addr(addr);	goto out_up;}static void unix_state_double_lock(struct sock *sk1, struct sock *sk2){	if (unlikely(sk1 == sk2) || !sk2) {		unix_state_lock(sk1);		return;	}	if (sk1 < sk2) {		unix_state_lock(sk1);		unix_state_lock_nested(sk2);	} else {		unix_state_lock(sk2);		unix_state_lock_nested(sk1);	}}static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2){	if (unlikely(sk1 == sk2) || !sk2) {		unix_state_unlock(sk1);		return;	}	unix_state_unlock(sk1);	unix_state_unlock(sk2);}static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,			      int alen, int flags){	struct sock *sk = sock->sk;	struct sockaddr_un *sunaddr=(struct sockaddr_un*)addr;	struct sock *other;	unsigned hash;	int err;	if (addr->sa_family != AF_UNSPEC) {		err = unix_mkname(sunaddr, alen, &hash);		if (err < 0)			goto out;		alen = err;		if (test_bit(SOCK_PASSCRED, &sock->flags) &&		    !unix_sk(sk)->addr && (err = unix_autobind(sock)) != 0)			goto out;restart:		other=unix_find_other(sunaddr, alen, sock->type, hash, &err);		if (!other)			goto out;		unix_state_double_lock(sk, other);		/* Apparently VFS overslept socket death. Retry. */		if (sock_flag(other, SOCK_DEAD)) {			unix_state_double_unlock(sk, other);			sock_put(other);			goto restart;		}		err = -EPERM;		if (!unix_may_send(sk, other))			goto out_unlock;		err = security_unix_may_send(sk->sk_socket, other->sk_socket);		if (err)			goto out_unlock;	} else {		/*		 *	1003.1g breaking connected state with AF_UNSPEC		 */		other = NULL;		unix_state_double_lock(sk, other);	}	/*	 * If it was connected, reconnect.	 */	if (unix_peer(sk)) {		struct sock *old_peer = unix_peer(sk);		unix_peer(sk)=other;		unix_state_double_unlock(sk, other);		if (other != old_peer)			unix_dgram_disconnected(sk, old_peer);		sock_put(old_peer);	} else {		unix_peer(sk)=other;		unix_state_double_unlock(sk, other);	}	return 0;out_unlock:	unix_state_double_unlock(sk, other);	sock_put(other);out:	return err;}static long unix_wait_for_peer(struct sock *other, long timeo){	struct unix_sock *u = unix_sk(other);	int sched;	DEFINE_WAIT(wait);	prepare_to_wait_exclusive(&u->peer_wait, &wait, TASK_INTERRUPTIBLE);	sched = !sock_flag(other, SOCK_DEAD) &&		!(other->sk_shutdown & RCV_SHUTDOWN) &&		(skb_queue_len(&other->sk_receive_queue) >		 other->sk_max_ack_backlog);	unix_state_unlock(other);	if (sched)		timeo = schedule_timeout(timeo);	finish_wait(&u->peer_wait, &wait);	return timeo;}static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,			       int addr_len, int flags){	struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;	struct sock *sk = sock->sk;	struct unix_sock *u = unix_sk(sk), *newu, *otheru;	struct sock *newsk = NULL;	struct sock *other = NULL;	struct sk_buff *skb = NULL;	unsigned hash;	int st;	int err;	long timeo;	err = unix_mkname(sunaddr, addr_len, &hash);	if (err < 0)		goto out;	addr_len = err;	if (test_bit(SOCK_PASSCRED, &sock->flags)		&& !u->addr && (err = unix_autobind(sock)) != 0)		goto out;	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);	/* First of all allocate resources.	   If we will make it after state is locked,	   we will have to recheck all again in any case.	 */	err = -ENOMEM;	/* create new sock for complete connection */	newsk = unix_create1(sk->sk_net, NULL);	if (newsk == NULL)		goto out;	/* Allocate skb for sending to listening sock */	skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);	if (skb == NULL)		goto out;restart:	/*  Find listening sock. */	other = unix_find_other(sunaddr, addr_len, sk->sk_type, hash, &err);	if (!other)		goto out;	/* Latch state of peer */	unix_state_lock(other);	/* Apparently VFS overslept socket death. Retry. */	if (sock_flag(other, SOCK_DEAD)) {		unix_state_unlock(other);		sock_put(other);		goto restart;	}	err = -ECONNREFUSED;	if (other->sk_state != TCP_LISTEN)		goto out_unlock;	if (skb_queue_len(&other->sk_receive_queue) >	    other->sk_max_ack_backlog) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -