📄 af_unix.c
字号:
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->sk_state != st) { unix_state_wunlock(sk); unix_state_runlock(other); sock_put(other); goto restart; } err = security_unix_stream_connect(sock, other->sk_socket, newsk); if (err) { unix_state_wunlock(sk); goto out_unlock; } /* The way is open! Fastly set all the necessary fields... */ sock_hold(sk); unix_peer(newsk) = sk; newsk->sk_state = TCP_ESTABLISHED; newsk->sk_type = sk->sk_type; newsk->sk_peercred.pid = current->tgid; newsk->sk_peercred.uid = current->euid; newsk->sk_peercred.gid = current->egid; newu = unix_sk(newsk); newsk->sk_sleep = &newu->peer_wait; otheru = unix_sk(other); /* copy address information from listening to new sock*/ if (otheru->addr) { atomic_inc(&otheru->addr->refcnt); newu->addr = otheru->addr; } if (otheru->dentry) { newu->dentry = dget(otheru->dentry); newu->mnt = mntget(otheru->mnt); } /* Set credentials */ sk->sk_peercred = other->sk_peercred; sock_hold(newsk); unix_peer(sk) = newsk; sock->state = SS_CONNECTED; sk->sk_state = TCP_ESTABLISHED; unix_state_wunlock(sk); /* take ten and and send info to listening sock */ spin_lock(&other->sk_receive_queue.lock); __skb_queue_tail(&other->sk_receive_queue, skb); /* Undo artificially decreased inflight after embrion * is installed to listening socket. */ atomic_inc(&newu->inflight); spin_unlock(&other->sk_receive_queue.lock); unix_state_runlock(other); other->sk_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->sk_peercred.pid = skb->sk_peercred.pid = current->tgid; ska->sk_peercred.uid = skb->sk_peercred.uid = current->euid; ska->sk_peercred.gid = skb->sk_peercred.gid = current->egid; if (ska->sk_type != SOCK_DGRAM) { ska->sk_state = TCP_ESTABLISHED; skb->sk_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){ struct sock *sk = sock->sk; struct sock *tsk; struct sk_buff *skb; int err; err = -EOPNOTSUPP; if (sock->type!=SOCK_STREAM && sock->type!=SOCK_SEQPACKET) goto out; err = -EINVAL; if (sk->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) { /* This means receive shutdown. */ if (err == 0) err = -EINVAL; goto out; } tsk = skb->sk; skb_free_datagram(sk, skb); wake_up_interruptible(&unix_sk(sk)->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 unix_sock *u; 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); } u = unix_sk(sk); unix_state_rlock(sk); if (!u->addr) { sunaddr->sun_family = AF_UNIX; sunaddr->sun_path[0] = 0; *uaddr_len = sizeof(short); } else { struct unix_address *addr = u->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 kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len){ struct sock_iocb *siocb = kiocb_to_siocb(kiocb); struct sock *sk = sock->sk; struct unix_sock *u = unix_sk(sk); struct sockaddr_un *sunaddr=msg->msg_name; struct sock *other = NULL; int namelen = 0; /* fake GCC */ int err; unsigned hash; struct sk_buff *skb; long timeo; struct scm_cookie tmp_scm; if (NULL == siocb->scm) siocb->scm = &tmp_scm; err = scm_send(sock, msg, siocb->scm); if (err < 0) return err; 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 && !u->addr && (err = unix_autobind(sock)) != 0) goto out; err = -EMSGSIZE; if (len > sk->sk_sndbuf - 32) goto out; skb = sock_alloc_send_skb(sk, len, msg->msg_flags&MSG_DONTWAIT, &err); if (skb==NULL) goto out; memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); if (siocb->scm->fp) unix_attach_fds(siocb->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->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 (sock_flag(other, SOCK_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->sk_shutdown & RCV_SHUTDOWN) goto out_unlock; err = security_unix_may_send(sk->sk_socket, other->sk_socket); if (err) goto out_unlock; if (unix_peer(other) != sk && (skb_queue_len(&other->sk_receive_queue) > other->sk_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->sk_receive_queue, skb); unix_state_runlock(other); other->sk_data_ready(other, len); sock_put(other); scm_destroy(siocb->scm); return len;out_unlock: unix_state_runlock(other);out_free: kfree_skb(skb);out: if (other) sock_put(other); scm_destroy(siocb->scm); return err;} static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len){ struct sock_iocb *siocb = kiocb_to_siocb(kiocb); struct sock *sk = sock->sk; struct sock *other = NULL; struct sockaddr_un *sunaddr=msg->msg_name; int err,size; struct sk_buff *skb; int sent=0; struct scm_cookie tmp_scm; if (NULL == siocb->scm) siocb->scm = &tmp_scm; err = scm_send(sock, msg, siocb->scm); if (err < 0) return err; err = -EOPNOTSUPP; if (msg->msg_flags&MSG_OOB) goto out_err; if (msg->msg_namelen) { err = sk->sk_state == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP; goto out_err; } else { sunaddr = NULL; err = -ENOTCONN; other = unix_peer_get(sk); if (!other) goto out_err; } if (sk->sk_shutdown & SEND_SHUTDOWN) goto pipe_err; while(sent < len) { /* * Optimisation for the fact that under 0.01% of X messages typically * need breaking up. */ size=len-sent; /* Keep two messages in the pipe so it schedules better */ if (size > sk->sk_sndbuf / 2 - 64) size = sk->sk_sndbuf / 2 - 64; if (size > SKB_MAX_ALLOC) size = SKB_MAX_ALLOC; /* * Grab a buffer */ skb=sock_alloc_send_skb(sk,size,msg->msg_flags&MSG_DONTWAIT, &err); if (skb==NULL) goto out_err; /* * If you pass two values to the sock_alloc_send_skb * it tries to grab the large buffer with GFP_NOFS * (which can fail easily), and if it fails grab the * fallback size buffer which is under a page and will * succeed. [Alan] */ size = min_t(int, size, skb_tailroom(skb)); memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); if (siocb->scm->fp) unix_attach_fds(siocb->scm, skb); if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) { kfree_skb(skb); goto out_err; } unix_state_rlock(other); if (sock_flag(other, SOCK_DEAD) || (other->sk_shutdown & RCV_SHUTDOWN)) goto pipe_err_free; skb_queue_tail(&other->sk_receive_queue, skb); unix_state_runlock(other); other->sk_data_ready(other, size); sent+=size; } sock_put(other); scm_destroy(siocb->scm); siocb->scm = NULL; return sent;pipe_err_free: unix_state_runlock(other); kfree_skb(skb);pipe_err: if (sent==0 && !(msg->msg_flags&MSG_NOSIGNAL)) send_sig(SIGPIPE,current,0); err = -EPIPE;out_err: if (other) sock_put(other); scm_destroy(siocb->scm); siocb->scm = NULL; return sent ? : err;}static void unix_copy_addr(struct msghdr *msg, struct sock *sk){ struct unix_sock *u = unix_sk(sk); msg->msg_namelen = 0; if (u->addr) { msg->msg_namelen = u->addr->len; memcpy(msg->msg_name, u->addr->name, u->addr->len); }}static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags){ struct sock_iocb *siocb = kiocb_to_siocb(iocb); struct scm_cookie tmp_scm; struct sock *sk = sock->sk; struct unix_sock *u = unix_sk(sk); int noblock = flags & MSG_DONTWAIT; struct sk_buff *skb; int err; err = -EOPNOTSUPP; if (flags&MSG_OOB) goto out; msg->msg_namelen = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -