📄 af_unix.c
字号:
}static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len){ struct sock *sk = sock->sk; struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; struct dentry * dentry = NULL; struct nameidata nd; int err; unsigned hash; struct unix_address *addr; unix_socket **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; down(&sk->protinfo.af_unix.readsem); err = -EINVAL; if (sk->protinfo.af_unix.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->type; atomic_set(&addr->refcnt, 1); if (sunaddr->sun_path[0]) { err = 0; /* * Get the parent directory, calculate the hash for last * component. */ if (path_init(sunaddr->sun_path, LOOKUP_PARENT, &nd)) err = path_walk(sunaddr->sun_path, &nd); if (err) goto out_mknod_parent; /* * Yucky last component or no last component at all? * (foo/., foo/.., /////) */ err = -EEXIST; if (nd.last_type != LAST_NORM) goto out_mknod; /* * Lock the directory. */ down(&nd.dentry->d_inode->i_sem); /* * Do the final lookup. */ dentry = lookup_hash(&nd.last, nd.dentry); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_mknod_unlock; err = -ENOENT; /* * Special case - lookup gave negative, but... we had foo/bar/ * From the vfs_mknod() POV we just have a negative dentry - * all is fine. Let's be bastards - you had / on the end, you've * been asking for (non-existent) directory. -ENOENT for you. */ if (nd.last.name[nd.last.len] && !dentry->d_inode) goto out_mknod_dput; /* * All right, let's create it. */ err = vfs_mknod(nd.dentry->d_inode, dentry, S_IFSOCK|sock->inode->i_mode, 0); if (err) goto out_mknod_dput; up(&nd.dentry->d_inode->i_sem); dput(nd.dentry); nd.dentry = dentry; addr->hash = UNIX_HASH_SIZE; } write_lock(&unix_table_lock); if (!sunaddr->sun_path[0]) { err = -EADDRINUSE; if (__unix_find_socket_byname(sunaddr, addr_len, 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)]; sk->protinfo.af_unix.dentry = nd.dentry; sk->protinfo.af_unix.mnt = nd.mnt; } err = 0; __unix_remove_socket(sk); sk->protinfo.af_unix.addr = addr; __unix_insert_socket(list, sk);out_unlock: write_unlock(&unix_table_lock);out_up: up(&sk->protinfo.af_unix.readsem);out: return err;out_mknod_dput: dput(dentry);out_mknod_unlock: up(&nd.dentry->d_inode->i_sem);out_mknod: path_release(&nd);out_mknod_parent: if (err==-EEXIST) err=-EADDRINUSE; unix_release_addr(addr); goto out_up;}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 (sock->passcred && !sk->protinfo.af_unix.addr && (err = unix_autobind(sock)) != 0) goto out; other=unix_find_other(sunaddr, alen, sock->type, hash, &err); if (!other) goto out; unix_state_wlock(sk); err = -EPERM; if (!unix_may_send(sk, other)) goto out_unlock; } else { /* * 1003.1g breaking connected state with AF_UNSPEC */ other = NULL; unix_state_wlock(sk); } /* * If it was connected, reconnect. */ if (unix_peer(sk)) { struct sock *old_peer = unix_peer(sk); unix_peer(sk)=other; unix_state_wunlock(sk); if (other != old_peer) unix_dgram_disconnected(sk, old_peer); sock_put(old_peer); } else { unix_peer(sk)=other; unix_state_wunlock(sk); } return 0;out_unlock: unix_state_wunlock(sk); sock_put(other);out: return err;}static long unix_wait_for_peer(unix_socket *other, long timeo){ int sched; DECLARE_WAITQUEUE(wait, current); __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue_exclusive(&other->protinfo.af_unix.peer_wait, &wait); sched = (!other->dead && !(other->shutdown&RCV_SHUTDOWN) && skb_queue_len(&other->receive_queue) > other->max_ack_backlog); unix_state_runlock(other); if (sched) timeo = schedule_timeout(timeo); __set_current_state(TASK_RUNNING); remove_wait_queue(&other->protinfo.af_unix.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 sock *newsk = NULL; unix_socket *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 (sock->passcred && !sk->protinfo.af_unix.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(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->type, hash, &err); if (!other) goto out; /* Latch state of peer */ unix_state_rlock(other); /* Apparently VFS overslept socket death. Retry. */ if (other->dead) { unix_state_runlock(other); sock_put(other); goto restart; } err = -ECONNREFUSED; if (other->state != TCP_LISTEN) goto out_unlock; if (skb_queue_len(&other->receive_queue) > other->max_ack_backlog) { err = -EAGAIN; if (!timeo) goto out_unlock; timeo = unix_wait_for_peer(other, timeo); err = sock_intr_errno(timeo); if (signal_pending(current)) goto out; sock_put(other); goto restart; } /* Latch our state. It is tricky place. We need to grab write lock and cannot drop lock on peer. It is dangerous because deadlock is possible. Connect to self case and simultaneous attempt to connect are eliminated by checking socket state. other is TCP_LISTEN, if sk is TCP_LISTEN we check this before attempt to grab lock. Well, and we have to recheck the state after socket locked. */ st = sk->state; switch (st) { case TCP_CLOSE: /* This is ok... continue with connect */ break; case TCP_ESTABLISHED: /* Socket is already connected */ err = -EISCONN; goto out_unlock; default: err = -EINVAL; goto out_unlock; } unix_state_wlock(sk); if (sk->state != st) { unix_state_wunlock(sk); unix_state_runlock(other); sock_put(other); goto restart; } /* The way is open! Fastly set all the necessary fields... */ sock_hold(sk); unix_peer(newsk)=sk; newsk->state=TCP_ESTABLISHED; newsk->type=SOCK_STREAM; newsk->peercred.pid = current->pid; newsk->peercred.uid = current->euid; newsk->peercred.gid = current->egid; newsk->sleep = &newsk->protinfo.af_unix.peer_wait; /* copy address information from listening to new sock*/ if (other->protinfo.af_unix.addr) { atomic_inc(&other->protinfo.af_unix.addr->refcnt); newsk->protinfo.af_unix.addr=other->protinfo.af_unix.addr; } if (other->protinfo.af_unix.dentry) { newsk->protinfo.af_unix.dentry=dget(other->protinfo.af_unix.dentry); newsk->protinfo.af_unix.mnt=mntget(other->protinfo.af_unix.mnt); } /* Set credentials */ sk->peercred = other->peercred; sock_hold(newsk); unix_peer(sk)=newsk; sock->state=SS_CONNECTED; sk->state=TCP_ESTABLISHED; unix_state_wunlock(sk); /* take ten and and send info to listening sock */ skb_queue_tail(&other->receive_queue,skb); unix_state_runlock(other); other->data_ready(other, 0); sock_put(other); return 0;out_unlock: if (other) unix_state_runlock(other);out: if (skb) kfree_skb(skb); if (newsk) unix_release_sock(newsk, 0); if (other) sock_put(other); return err;}static int unix_socketpair(struct socket *socka, struct socket *sockb){ struct sock *ska=socka->sk, *skb = sockb->sk; /* Join our sockets back to back */ sock_hold(ska); sock_hold(skb); unix_peer(ska)=skb; unix_peer(skb)=ska; ska->peercred.pid = skb->peercred.pid = current->pid; ska->peercred.uid = skb->peercred.uid = current->euid; ska->peercred.gid = skb->peercred.gid = current->egid; if (ska->type != SOCK_DGRAM) { ska->state=TCP_ESTABLISHED; skb->state=TCP_ESTABLISHED; socka->state=SS_CONNECTED; sockb->state=SS_CONNECTED; } return 0;}static int unix_accept(struct socket *sock, struct socket *newsock, int flags){ unix_socket *sk = sock->sk; unix_socket *tsk; struct sk_buff *skb; int err; err = -EOPNOTSUPP; if (sock->type!=SOCK_STREAM) goto out; err = -EINVAL; if (sk->state!=TCP_LISTEN) goto out; /* If socket state is TCP_LISTEN it cannot change (for now...), * so that no locks are necessary. */ skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err); if (!skb) goto out; tsk = skb->sk; skb_free_datagram(sk, skb); wake_up_interruptible(&sk->protinfo.af_unix.peer_wait); /* attach accepted sock to socket */ unix_state_wlock(tsk); newsock->state = SS_CONNECTED; sock_graft(tsk, newsock); unix_state_wunlock(tsk); return 0;out: return err;}static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer){ struct sock *sk = sock->sk; struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; int err = 0; if (peer) { sk = unix_peer_get(sk); err = -ENOTCONN; if (!sk) goto out; err = 0; } else { sock_hold(sk); } unix_state_rlock(sk); if (!sk->protinfo.af_unix.addr) { sunaddr->sun_family = AF_UNIX; sunaddr->sun_path[0] = 0; *uaddr_len = sizeof(short); } else { struct unix_address *addr = sk->protinfo.af_unix.addr; *uaddr_len = addr->len; memcpy(sunaddr, addr->name, *uaddr_len); } unix_state_runlock(sk); sock_put(sk);out: return err;}static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb){ int i; scm->fp = UNIXCB(skb).fp; skb->destructor = sock_wfree; UNIXCB(skb).fp = NULL; for (i=scm->fp->count-1; i>=0; i--) unix_notinflight(scm->fp->fp[i]);}static void unix_destruct_fds(struct sk_buff *skb){ struct scm_cookie scm; memset(&scm, 0, sizeof(scm)); unix_detach_fds(&scm, skb); /* Alas, it calls VFS */ /* So fscking what? fput() had been SMP-safe since the last Summer */ scm_destroy(&scm); sock_wfree(skb);}static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb){ int i; for (i=scm->fp->count-1; i>=0; i--) unix_inflight(scm->fp->fp[i]); UNIXCB(skb).fp = scm->fp; skb->destructor = unix_destruct_fds; scm->fp = NULL;}/* * Send AF_UNIX data. */static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm){ struct sock *sk = sock->sk; struct sockaddr_un *sunaddr=msg->msg_name; unix_socket *other = NULL; int namelen = 0; /* fake GCC */ int err; unsigned hash; struct sk_buff *skb; long timeo; err = -EOPNOTSUPP; if (msg->msg_flags&MSG_OOB) goto out; if (msg->msg_namelen) { err = unix_mkname(sunaddr, msg->msg_namelen, &hash); if (err < 0) goto out; namelen = err; } else { sunaddr = NULL; err = -ENOTCONN; other = unix_peer_get(sk); if (!other) goto out; } if (sock->passcred && !sk->protinfo.af_unix.addr && (err = unix_autobind(sock)) != 0) goto out; err = -EMSGSIZE; if ((unsigned)len > sk->sndbuf - 32) goto out; skb = sock_alloc_send_skb(sk, len, 0, msg->msg_flags&MSG_DONTWAIT, &err); if (skb==NULL) goto out; memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred)); if (scm->fp) unix_attach_fds(scm, skb); skb->h.raw = skb->data; err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); if (err) goto out_free; timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);restart: if (!other) { err = -ECONNRESET; if (sunaddr == NULL) goto out_free; other = unix_find_other(sunaddr, namelen, sk->type, hash, &err); if (other==NULL) goto out_free; } unix_state_rlock(other); err = -EPERM; if (!unix_may_send(sk, other)) goto out_unlock; if (other->dead) { /* * Check with 1003.1g - what should * datagram error */ unix_state_runlock(other); sock_put(other); err = 0; unix_state_wlock(sk); if (unix_peer(sk) == other) { unix_peer(sk)=NULL; unix_state_wunlock(sk); unix_dgram_disconnected(sk, other); sock_put(other); err = -ECONNREFUSED; } else { unix_state_wunlock(sk); } other = NULL; if (err) goto out_free; goto restart; } err = -EPIPE; if (other->shutdown&RCV_SHUTDOWN) goto out_unlock; if (unix_peer(other) != sk && skb_queue_len(&other->receive_queue) > other->max_ack_backlog) { if (!timeo) { err = -EAGAIN; goto out_unlock; } timeo = unix_wait_for_peer(other, timeo); err = sock_intr_errno(timeo); if (signal_pending(current)) goto out_free; goto restart; } skb_queue_tail(&other->receive_queue, skb);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -