Lemur zaprasza
net/unix/af_unix.c /* * NET3: Implementation of BSD Unix domain sockets. * * Authors: Alan Cox, <alan@cymru.net> * * Currently this contains all but the file descriptor passing code. * Before that goes in the odd bugs in the iovec handlers need * fixing, and this bit testing. BSD fd passing is not a trivial part * of the exercise it turns out. Anyone like writing garbage collectors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Fixes: * Linus Torvalds : Assorted bug cures. * Niibe Yutaka : async I/O support. * Carsten Paeth : PF_UNIX check, address fixes. * Alan Cox : Limit size of allocated blocks. * Alan Cox : Fixed the stupid socketpair bug. * Alan Cox : BSD compatibility fine tuning. * Alan Cox : Fixed a bug in connect when interrupted. * Alan Cox : Sorted out a proper draft version of * file descriptor passing hacked up from * Mike Shaver's work. * Marty Leisner : Fixes to fd passing * Nick Nevin : recvmsg bugfix. * Alan Cox : Started proper garbage collector * Heiko EiBfeldt : Missing verify_area check * * Known differences from reference BSD that was tested: * * [TO FIX] * ECONNREFUSED is not returned from one end of a connected() socket to the * other the moment one end closes. * fstat() doesn't return st_dev=NODEV, and give the blksize as high water mark * and a fake inode identifier (nor the BSD first socket fstat twice bug). * [NOT TO FIX] * accept() returns a path name even if the connecting socket has closed * in the meantime (BSD loses the path and gives up). * accept() returns 0 length path for an unbound connector. BSD returns 16 * and a null first byte in the path (but not for gethost/peername - BSD bug ??) * socketpair(...SOCK_RAW..) doesn't panic the kernel. * BSD af_unix apparently has connect forgetting to block properly. */ #include <linux/config.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/stat.h> #include <linux/socket.h> #include <linux/un.h> #include <linux/fcntl.h> #include <linux/termios.h> #include <linux/socket.h> #include <linux/sockios.h> #include <linux/net.h> #include <linux/in.h> #include <linux/fs.h> #include <linux/malloc.h> #include <asm/segment.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <net/sock.h> #include <net/tcp.h> #include <net/af_unix.h> #include <linux/proc_fs.h> unix_socket *unix_socket_list=NULL; #define min(a,b) (((a)<(b))?(a):(b)) /* * Make sure the unix name is null-terminated. */ static inline void unix_mkname(struct sockaddr_un * sunaddr, unsigned long len) { if (len >= sizeof(*sunaddr)) len = sizeof(*sunaddr)-1; ((char *)sunaddr)[len]=0; } /* * Note: Sockets may not be removed _during_ an interrupt or net_bh * handler using this technique. They can be added although we do not * use this facility. */ static void unix_remove_socket(unix_socket *sk) { unix_socket **s; cli(); s=&unix_socket_list; while(*s!=NULL) { if(*s==sk) { *s=sk->next; sti(); return; } s=&((*s)->next); } sti(); } static void unix_insert_socket(unix_socket *sk) { cli(); sk->next=unix_socket_list; unix_socket_list=sk; sti(); } static unix_socket *unix_find_socket(struct inode *i) { unix_socket *s; cli(); s=unix_socket_list; while(s) { if(s->protinfo.af_unix.inode==i) { sti(); return(s); } s=s->next; } sti(); return(NULL); } /* * Delete a unix socket. We have to allow for deferring this on a timer. */ static void unix_destroy_timer(unsigned long data) { unix_socket *sk=(unix_socket *)data; if(sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0) { if(sk->protinfo.af_unix.name) kfree(sk->protinfo.af_unix.name); sk_free(sk); return; } /* * Retry; */ sk->timer.expires=jiffies+10*HZ; /* No real hurry try it every 10 seconds or so */ add_timer(&sk->timer); } static void unix_delayed_delete(unix_socket *sk) { sk->timer.data=(unsigned long)sk; sk->timer.expires=jiffies+HZ; /* Normally 1 second after will clean up. After that we try every 10 */ sk->timer.function=unix_destroy_timer; add_timer(&sk->timer); } static void unix_destroy_socket(unix_socket *sk) { struct sk_buff *skb; unix_remove_socket(sk); while((skb=skb_dequeue(&sk->receive_queue))!=NULL) { if(sk->state==TCP_LISTEN) { unix_socket *osk=skb->sk; osk->state=TCP_CLOSE; kfree_skb(skb, FREE_WRITE); /* Now surplus - free the skb first before the socket */ osk->state_change(osk); /* So the connect wakes and cleans up (if any) */ /* osk will be destroyed when it gets to close or the timer fires */ } else { /* passed fds are erased in the kfree_skb hook */ kfree_skb(skb,FREE_WRITE); } } if(sk->protinfo.af_unix.inode!=NULL) { iput(sk->protinfo.af_unix.inode); sk->protinfo.af_unix.inode=NULL; } if(--sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0) { if(sk->protinfo.af_unix.name) kfree(sk->protinfo.af_unix.name); sk_free(sk); } else { sk->dead=1; unix_delayed_delete(sk); /* Try every so often until buffers are all freed */ } } /* * Fixme: We need async I/O on AF_UNIX doing next. */ static int unix_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) { return -EINVAL; } /* * Yes socket options work with the new unix domain socketry!!!!!!! */ static int unix_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { unix_socket *sk=sock->data; if(level!=SOL_SOCKET) return -EOPNOTSUPP; return sock_setsockopt(sk,level,optname,optval,optlen); } static int unix_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { unix_socket *sk=sock->data; if(level!=SOL_SOCKET) return -EOPNOTSUPP; return sock_getsockopt(sk,level,optname,optval,optlen); } static int unix_listen(struct socket *sock, int backlog) { unix_socket *sk=sock->data; if(sk->type!=SOCK_STREAM) return -EOPNOTSUPP; /* Only stream sockets accept */ if(sk->protinfo.af_unix.name==NULL) return -EINVAL; /* No listens on an unbound socket */ sk->max_ack_backlog=backlog; sk->state=TCP_LISTEN; return 0; } static void def_callback1(struct sock *sk) { if(!sk->dead) wake_up_interruptible(sk->sleep); } static void def_callback2(struct sock *sk, int len) { if(!sk->dead) { wake_up_interruptible(sk->sleep); sock_wake_async(sk->socket, 1); } } static void def_callback3(struct sock *sk) { if(!sk->dead) { wake_up_interruptible(sk->sleep); sock_wake_async(sk->socket, 2); } } static int unix_create(struct socket *sock, int protocol) { unix_socket *sk; if(protocol && protocol != PF_UNIX) return -EPROTONOSUPPORT; sk=(unix_socket *)sk_alloc(GFP_KERNEL); if(sk==NULL) return -ENOMEM; switch(sock->type) { case SOCK_STREAM: 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: break; default: sk_free(sk); return -ESOCKTNOSUPPORT; } sk->type=sock->type; init_timer(&sk->timer); skb_queue_head_init(&sk->write_queue); skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->back_log); sk->protinfo.af_unix.family=AF_UNIX; sk->protinfo.af_unix.inode=NULL; sk->protinfo.af_unix.locks=1; /* Us */ sk->protinfo.af_unix.readsem=MUTEX; /* single task reading lock */ sk->rcvbuf=SK_RMEM_MAX; sk->sndbuf=SK_WMEM_MAX; sk->allocation=GFP_KERNEL; sk->state=TCP_CLOSE; sk->priority=SOPRI_NORMAL; sk->state_change=def_callback1; sk->data_ready=def_callback2; sk->write_space=def_callback3; sk->error_report=def_callback1; sk->mtu=4096; sk->socket=sock; sock->data=(void *)sk; sk->sleep=sock->wait; unix_insert_socket(sk); return 0; } static int unix_dup(struct socket *newsock, struct socket *oldsock) { return unix_create(newsock,0); } static int unix_release(struct socket *sock, struct socket *peer) { unix_socket *sk=sock->data; unix_socket *skpair; /* May not have data attached */ if(sk==NULL) return 0; sk->state_change(sk); sk->dead=1; skpair=(unix_socket *)sk->protinfo.af_unix.other; /* Person we send to (default) */ if(sk->type==SOCK_STREAM && skpair!=NULL && skpair->state!=TCP_LISTEN) { skpair->shutdown=SHUTDOWN_MASK; /* No more writes */ skpair->state_change(skpair); /* Wake any blocked writes */ } if(skpair!=NULL) skpair->protinfo.af_unix.locks--; /* It may now die */ sk->protinfo.af_unix.other=NULL; /* No pair */ unix_destroy_socket(sk); /* Try to flush out this socket. Throw out buffers at least */ unix_gc(); /* Garbage collect fds */ /* * FIXME: BSD difference: In BSD all sockets connected to use get ECONNRESET and we die on the spot. In * Linux we behave like files and pipes do and wait for the last dereference. */ sock->data = NULL; sk->socket = NULL; return 0; } static unix_socket *unix_find_other(char *path, int *error) { int old_fs; int err; struct inode *inode; unix_socket *u; old_fs=get_fs(); set_fs(get_ds()); err = open_namei(path, 2, S_IFSOCK, &inode, NULL); set_fs(old_fs); if(err<0) { *error=err; return NULL; } u=unix_find_socket(inode); iput(inode); if(u==NULL) { *error=-ECONNREFUSED; return NULL; } return u; } static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; unix_socket *sk=sock->data; int old_fs; int err; if(sk->protinfo.af_unix.name) return -EINVAL; /* Already bound */ if(addr_len>sizeof(struct sockaddr_un) || addr_len<3 || sunaddr->sun_family!=AF_UNIX) return -EINVAL; unix_mkname(sunaddr, addr_len); /* * Put ourselves in the filesystem */ if(sk->protinfo.af_unix.inode!=NULL) return -EINVAL; sk->protinfo.af_unix.name=kmalloc(addr_len+1, GFP_KERNEL); if(sk->protinfo.af_unix.name==NULL) return -ENOMEM; memcpy(sk->protinfo.af_unix.name, sunaddr->sun_path, addr_len+1); old_fs=get_fs(); set_fs(get_ds()); err=do_mknod(sk->protinfo.af_unix.name,S_IFSOCK|S_IRWXUGO,0); if(err==0) err=open_namei(sk->protinfo.af_unix.name, 2, S_IFSOCK, &sk->protinfo.af_unix.inode, NULL); set_fs(old_fs); if(err<0) { kfree_s(sk->protinfo.af_unix.name,addr_len+1); sk->protinfo.af_unix.name=NULL; if(err==-EEXIST) return -EADDRINUSE; else return err; } return 0; } static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { unix_socket *sk=sock->data; struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; unix_socket *other; struct sk_buff *skb; int err; if(sk->type==SOCK_STREAM && sk->protinfo.af_unix.other) { if(sock->state==SS_CONNECTING && sk->state==TCP_ESTABLISHED) { sock->state=SS_CONNECTED; return 0; } if(sock->state==SS_CONNECTING && sk->state == TCP_CLOSE) { sock->state=SS_UNCONNECTED; return -ECONNREFUSED; } if(sock->state!=SS_CONNECTING) return -EISCONN; if(flags&O_NONBLOCK) return -EALREADY; /* * Drop through the connect up logic to the wait. */ } if(addr_len < sizeof(sunaddr->sun_family)+1 || sunaddr->sun_family!=AF_UNIX) return -EINVAL; unix_mkname(sunaddr, addr_len); if(sk->type==SOCK_DGRAM) { if(sk->protinfo.af_unix.other) { sk->protinfo.af_unix.other->protinfo.af_unix.locks--; sk->protinfo.af_unix.other=NULL; sock->state=SS_UNCONNECTED; } other=unix_find_other(sunaddr->sun_path, &err); if(other==NULL) return err; if(other->type!=sk->type) return -EPROTOTYPE; other->protinfo.af_unix.locks++; sk->protinfo.af_unix.other=other; sock->state=SS_CONNECTED; sk->state=TCP_ESTABLISHED; return 0; /* Done */ } if(sock->state==SS_UNCONNECTED) { /* * Now ready to connect */ skb=sock_alloc_send_skb(sk, 0, 0, 0, &err); /* Marker object */ if(skb==NULL) return err; skb->sk=sk; /* So they know it is us */ skb->free=1; skb->h.filp=NULL; sk->state=TCP_CLOSE; unix_mkname(sunaddr, addr_len); other=unix_find_other(sunaddr->sun_path, &err); if(other==NULL) { kfree_skb(skb, FREE_WRITE); return err; } if(other->type!=sk->type) { kfree_skb(skb, FREE_WRITE); return -EPROTOTYPE; } other->protinfo.af_unix.locks++; /* Lock the other socket so it doesn't run off for a moment */ other->ack_backlog++; sk->protinfo.af_unix.other=other; skb_queue_tail(&other->receive_queue,skb); sk->state=TCP_SYN_SENT; sock->state=SS_CONNECTING; sti(); other->data_ready(other,0); /* Wake up ! */ } /* Wait for an accept */ cli(); while(sk->state==TCP_SYN_SENT) { if(flags&O_NONBLOCK) { sti(); return -EINPROGRESS; } interruptible_sleep_on(sk->sleep); if(current->signal & ~current->blocked) { sti(); return -ERESTARTSYS; } } /* * Has the other end closed on us ? */ if(sk->state==TCP_CLOSE) { sk->protinfo.af_unix.other->protinfo.af_unix.locks--; sk->protinfo.af_unix.other=NULL; sock->state=SS_UNCONNECTED; sti(); return -ECONNREFUSED; } /* * Amazingly it has worked */ sock->state=SS_CONNECTED; sti(); return 0; } static int unix_socketpair(struct socket *a, struct socket *b) { unix_socket *ska,*skb; ska=a->data; skb=b->data; /* Join our sockets back to back */ ska->protinfo.af_unix.locks++; skb->protinfo.af_unix.locks++; ska->protinfo.af_unix.other=skb; skb->protinfo.af_unix.other=ska; ska->state=TCP_ESTABLISHED; skb->state=TCP_ESTABLISHED; return 0; } static int unix_accept(struct socket *sock, struct socket *newsock, int flags) { unix_socket *sk=sock->data; unix_socket *newsk, *tsk; struct sk_buff *skb; if(sk->type!=SOCK_STREAM) { return -EOPNOTSUPP; } if(sk->state!=TCP_LISTEN) { return -EINVAL; } newsk=newsock->data; if(sk->protinfo.af_unix.name!=NULL) { newsk->protinfo.af_unix.name=kmalloc(strlen(sk->protinfo.af_unix.name)+1, GFP_KERNEL); if(newsk->protinfo.af_unix.name==NULL) return -ENOMEM; strcpy(newsk->protinfo.af_unix.name, sk->protinfo.af_unix.name); } do { cli(); skb=skb_dequeue(&sk->receive_queue); if(skb==NULL) { if(flags&O_NONBLOCK) { sti(); return -EAGAIN; } interruptible_sleep_on(sk->sleep); if(current->signal & ~current->blocked) { sti(); return -ERESTARTSYS; } sti(); } } while(skb==NULL); tsk=skb->sk; kfree_skb(skb, FREE_WRITE); /* The buffer is just used as a tag */ sk->ack_backlog--; newsk->protinfo.af_unix.other=tsk; tsk->protinfo.af_unix.other=newsk; tsk->state=TCP_ESTABLISHED; newsk->state=TCP_ESTABLISHED; newsk->protinfo.af_unix.locks++; /* Swap lock over */ sk->protinfo.af_unix.locks--; /* Locked to child socket not master */ tsk->protinfo.af_unix.locks++; /* Back lock */ sti(); tsk->state_change(tsk); /* Wake up any sleeping connect */ sock_wake_async(tsk->socket, 0); return 0; } static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { unix_socket *sk=sock->data; struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; if(peer) { if(sk->protinfo.af_unix.other==NULL) return -ENOTCONN; sk=sk->protinfo.af_unix.other; } sunaddr->sun_family=AF_UNIX; if(sk->protinfo.af_unix.name==NULL) { *sunaddr->sun_path=0; *uaddr_len=sizeof(sunaddr->sun_family)+1; return 0; /* Not bound */ } *uaddr_len=sizeof(sunaddr->sun_family)+strlen(sk->protinfo.af_unix.name)+1; strcpy(sunaddr->sun_path,sk->protinfo.af_unix.name); /* 108 byte limited */ return 0; } /* * Support routines for struct cmsghdr handling */ static struct cmsghdr *unix_copyrights(void *userp, int len) { struct cmsghdr *cm; if(len>256|| len <=0) return NULL; cm=kmalloc(len, GFP_KERNEL); memcpy_fromfs(cm, userp, len); return cm; } /* * Return a header block */ static void unix_returnrights(void *userp, int len, struct cmsghdr *cm) { memcpy_tofs(userp, cm, len); kfree(cm); } /* * Copy file descriptors into system space. * Return number copied or negative error code */ static int unix_fd_copy(struct sock *sk, struct cmsghdr *cmsg, struct file **fp) { int num=cmsg->cmsg_len-sizeof(struct cmsghdr); int i; int *fdp=(int *)cmsg->cmsg_data; num/=4; /* Odd bytes are forgotten in BSD not errored */ if(num>=UNIX_MAX_FD) return -EINVAL; /* * Verify the descriptors. */ for(i=0; i< num; i++) { int fd; fd = fdp[i]; #if 0 printk("testing fd %d\n", fd); #endif if(fd < 0|| fd >=NR_OPEN) return -EBADF; if(current->files->fd[fd]==NULL) return -EBADF; } /* add another reference to these files */ for(i=0; i< num; i++) { fp[i]=current->files->fd[fdp[i]]; fp[i]->f_count++; unix_inflight(fp[i]); } return num; } /* * Free the descriptors in the array */ static void unix_fd_free(struct sock *sk, struct file **fp, int num) { int i; for(i=0;i<num;i++) { close_fp(fp[i]); unix_notinflight(fp[i]); } } /* * Count the free descriptors available to a process. * Interpretation issue: Is the limit the highest descriptor (buggy * allowing passed fd's higher up to cause a limit to be exceeded) - * but how the old code did it - or like this... */ static int unix_files_free(void) { int i; int n=0; for (i=0;i<NR_OPEN;i++) { if(current->files->fd[i]) n++; } i=NR_OPEN; if(i>current->rlim[RLIMIT_NOFILE].rlim_cur) i=current->rlim[RLIMIT_NOFILE].rlim_cur; if(n>=i) return 0; return i-n; } /* * Perform the AF_UNIX file descriptor pass out functionality. This * is nasty and messy as is the whole design of BSD file passing. */ static void unix_detach_fds(struct sk_buff *skb, struct cmsghdr *cmsg) { int i; /* count of space in parent for fds */ int cmnum; struct file **fp; struct file **ufp; int *cmfptr=NULL; /* =NULL To keep gcc happy */ /* number of fds actually passed */ int fdnum; int ffree; int ufn=0; if(cmsg==NULL) cmnum=0; else { cmnum=cmsg->cmsg_len-sizeof(struct cmsghdr); cmnum/=sizeof(int); cmfptr=(int *)&cmsg->cmsg_data; } memcpy(&fdnum,skb->h.filp,sizeof(int)); fp=(struct file **)(skb->h.filp+sizeof(int)); if(cmnum>fdnum) cmnum=fdnum; ffree=unix_files_free(); if(cmnum>ffree) cmnum=ffree; ufp=¤t->files->fd[0]; /* * Copy those that fit */ for(i=0;i<cmnum;i++) { /* * Insert the fd */ while(ufp[ufn]!=NULL) ufn++; ufp[ufn]=fp[i]; *cmfptr++=ufn; FD_CLR(ufn,¤t->files->close_on_exec); unix_notinflight(fp[i]); } /* * Dump those that don't */ for(;i<fdnum;i++) { close_fp(fp[i]); unix_notinflight(fp[i]); } kfree(skb->h.filp); skb->h.filp=NULL; /* no need to use destructor */ skb->destructor = NULL; } static void unix_destruct_fds(struct sk_buff *skb) { unix_detach_fds(skb,NULL); } /* * Attach the file descriptor array to an sk_buff */ static void unix_attach_fds(int fpnum,struct file **fp,struct sk_buff *skb) { skb->h.filp=kmalloc(sizeof(int)+fpnum*sizeof(struct file *), GFP_KERNEL); /* number of descriptors starts block */ memcpy(skb->h.filp,&fpnum,sizeof(int)); /* actual descriptors */ memcpy(skb->h.filp+sizeof(int),fp,fpnum*sizeof(struct file *)); skb->destructor = unix_destruct_fds; } /* * Send AF_UNIX data. */ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags) { unix_socket *sk=sock->data; unix_socket *other; struct sockaddr_un *sunaddr=msg->msg_name; int err,size; struct sk_buff *skb; int limit=0; int sent=0; struct file *fp[UNIX_MAX_FD]; /* number of fds waiting to be passed, 0 means either * no fds to pass or they've already been passed */ int fpnum=0; if(sk->err) return sock_error(sk); if(flags&MSG_OOB) return -EOPNOTSUPP; if(flags) /* For now */ { return -EINVAL; } if(sunaddr!=NULL) { if(sock->type==SOCK_STREAM) { if(sk->state==TCP_ESTABLISHED) return -EISCONN; else return -EOPNOTSUPP; } } if(sunaddr==NULL) { if(sk->protinfo.af_unix.other==NULL) return -ENOTCONN; } /* * A control message has been attached. */ if(msg->msg_control) { struct cmsghdr *cm=unix_copyrights(msg->msg_control, msg->msg_controllen); if(cm==NULL || msg->msg_controllen<sizeof(struct cmsghdr) || cm->cmsg_type!=SCM_RIGHTS || cm->cmsg_level!=SOL_SOCKET || msg->msg_controllen!=cm->cmsg_len) { kfree(cm); return -EINVAL; } fpnum=unix_fd_copy(sk,cm,fp); kfree(cm); if(fpnum<0) { return fpnum; } } while(sent < len) { /* * Optimisation for the fact that under 0.01% of X messages typically * need breaking up. */ size=len-sent; if(size>(sk->sndbuf-sizeof(struct sk_buff))/2) /* Keep two messages in the pipe so it schedules better */ { if(sock->type==SOCK_DGRAM) { unix_fd_free(sk,fp,fpnum); return -EMSGSIZE; } size=(sk->sndbuf-sizeof(struct sk_buff))/2; } /* * Keep to page sized kmalloc()'s as various people * have suggested. Big mallocs stress the vm too * much. */ if(size > 4000 && sock->type!=SOCK_DGRAM) limit = 4000; /* Fall back to 4K if we can't grab a big buffer this instant */ else limit = 0; /* Otherwise just grab and wait */ /* * Grab a buffer */ skb=sock_alloc_send_skb(sk,size,limit,nonblock, &err); if(skb==NULL) { unix_fd_free(sk,fp,fpnum); if(sent) { sk->err=-err; return sent; } return err; } size=skb_tailroom(skb); /* If we dropped back on a limit then our skb is smaller */ skb->sk=sk; skb->free=1; if(fpnum) { unix_attach_fds(fpnum,fp,skb); fpnum=0; } else skb->h.filp=NULL; memcpy_fromiovec(skb_put(skb,size),msg->msg_iov, size); cli(); if(sunaddr==NULL) { other=sk->protinfo.af_unix.other; if(sock->type==SOCK_DGRAM && other->dead) { other->protinfo.af_unix.locks--; sk->protinfo.af_unix.other=NULL; sock->state=SS_UNCONNECTED; sti(); kfree_skb(skb, FREE_WRITE); if(!sent) return -ECONNRESET; else return sent; } } else { unix_mkname(sunaddr, msg->msg_namelen); other=unix_find_other(sunaddr->sun_path, &err); if(other==NULL) { sti(); kfree_skb(skb, FREE_WRITE); if(sent) return sent; else return err; } } skb_queue_tail(&other->receive_queue, skb); sti(); /* if we sent an fd, only do it once */ other->data_ready(other,size); sent+=size; } return sent; } /* * Sleep until data has arrive. But check for races.. */ static void unix_data_wait(unix_socket * sk) { cli(); if (!skb_peek(&sk->receive_queue)) { sk->socket->flags |= SO_WAITDATA; interruptible_sleep_on(sk->sleep); sk->socket->flags &= ~SO_WAITDATA; } sti(); } static int unix_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len) { unix_socket *sk=sock->data; struct sockaddr_un *sunaddr=msg->msg_name; struct sk_buff *skb; int copied=0; unsigned char *sp; int len; int num; struct iovec *iov=msg->msg_iov; struct cmsghdr *cm=NULL; int ct=msg->msg_iovlen; if(flags&MSG_OOB) return -EOPNOTSUPP; if(addr_len) *addr_len=0; if(sk->err) return sock_error(sk); if(msg->msg_control) { cm=unix_copyrights(msg->msg_control, msg->msg_controllen); if(msg->msg_controllen<sizeof(struct cmsghdr) #if 0 /* investigate this further -- Stevens example doesn't seem to care */ || cm->cmsg_type!=SCM_RIGHTS || cm->cmsg_level!=SOL_SOCKET || msg->msg_controllen!=cm->cmsg_len #endif ) { kfree(cm); /* printk("recvmsg: Bad msg_control\n");*/ return -EINVAL; } } down(&sk->protinfo.af_unix.readsem); /* Lock the socket */ while(ct--) { int done=0; sp=iov->iov_base; len=iov->iov_len; iov++; while(done<len) { if (copied && (flags & MSG_PEEK)) goto out; if (copied == size) goto out; skb=skb_dequeue(&sk->receive_queue); if(skb==NULL) { up(&sk->protinfo.af_unix.readsem); if(sk->shutdown & RCV_SHUTDOWN) return copied; if(copied) return copied; if(noblock) return -EAGAIN; if(current->signal & ~current->blocked) return -ERESTARTSYS; unix_data_wait(sk); down(&sk->protinfo.af_unix.readsem); continue; } if(msg->msg_name!=NULL) { sunaddr->sun_family=AF_UNIX; if(skb->sk->protinfo.af_unix.name) { memcpy(sunaddr->sun_path, skb->sk->protinfo.af_unix.name, 108); if(addr_len) *addr_len=strlen(sunaddr->sun_path)+sizeof(short); } else if(addr_len) *addr_len=sizeof(short); } num=min(skb->len,len-done); memcpy_tofs(sp, skb->data, num); if (skb->h.filp!=NULL) unix_detach_fds(skb,cm); copied+=num; done+=num; sp+=num; if (!(flags & MSG_PEEK)) skb_pull(skb, num); /* put the skb back if we didn't use it up.. */ if (skb->len) { skb_queue_head(&sk->receive_queue, skb); continue; } kfree_skb(skb, FREE_WRITE); if(sock->type==SOCK_DGRAM || cm) goto out; } } out: up(&sk->protinfo.af_unix.readsem); if(cm) unix_returnrights(msg->msg_control,msg->msg_controllen,cm); return copied; } static int unix_shutdown(struct socket *sock, int mode) { unix_socket *sk=(unix_socket *)sock->data; unix_socket *other=sk->protinfo.af_unix.other; if(mode&SEND_SHUTDOWN) { sk->shutdown|=SEND_SHUTDOWN; sk->state_change(sk); if(other) { other->shutdown|=RCV_SHUTDOWN; other->state_change(other); } } other=sk->protinfo.af_unix.other; if(mode&RCV_SHUTDOWN) { sk->shutdown|=RCV_SHUTDOWN; sk->state_change(sk); if(other) { other->shutdown|=SEND_SHUTDOWN; other->state_change(other); } } return 0; } static int unix_select(struct socket *sock, int sel_type, select_table *wait) { return datagram_select(sock->data,sel_type,wait); } static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { unix_socket *sk=sock->data; int err; long amount=0; switch(cmd) { case TIOCOUTQ: err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long)); if(err) return err; amount=sk->sndbuf-sk->wmem_alloc; if(amount<0) amount=0; put_fs_long(amount,(unsigned long *)arg); return 0; case TIOCINQ: { struct sk_buff *skb; if(sk->state==TCP_LISTEN) return -EINVAL; /* These two are safe on a single CPU system as only user tasks fiddle here */ if((skb=skb_peek(&sk->receive_queue))!=NULL) amount=skb->len; err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long)); if(err) return err; put_fs_long(amount,(unsigned long *)arg); return 0; } default: return -EINVAL; } /*NOTREACHED*/ return(0); } #ifdef CONFIG_PROC_FS static int unix_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { off_t pos=0; off_t begin=0; int len=0; unix_socket *s=unix_socket_list; len+= sprintf(buffer,"Num RefCount Protocol Flags Type St " "Inode Path\n"); while(s!=NULL) { len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X %5ld", s, s->protinfo.af_unix.locks, 0, s->socket->flags, s->socket->type, s->socket->state, s->socket->inode ? s->socket->inode->i_ino : 0); if(s->protinfo.af_unix.name!=NULL) len+=sprintf(buffer+len, " %s\n", s->protinfo.af_unix.name); else buffer[len++]='\n'; pos=begin+len; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) break; s=s->next; } *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; return len; } #endif struct proto_ops unix_proto_ops = { AF_UNIX, unix_create, unix_dup, unix_release, unix_bind, unix_connect, unix_socketpair, unix_accept, unix_getname, unix_select, unix_ioctl, unix_listen, unix_shutdown, unix_setsockopt, unix_getsockopt, unix_fcntl, unix_sendmsg, unix_recvmsg }; void unix_proto_init(struct net_proto *pro) { printk(KERN_INFO "NET3: Unix domain sockets 0.12 for Linux NET3.035.\n"); sock_register(unix_proto_ops.family, &unix_proto_ops); #ifdef CONFIG_PROC_FS proc_net_register(&(struct proc_dir_entry) { PROC_NET_UNIX, 4, "unix", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, unix_get_info }); #endif } /* * Local variables: * compile-command: "gcc -g -D__KERNEL__ -Wall -O6 -I/usr/src/linux/include -c af_unix.c" * End: */ </body> |