* Alan Cox : Started proper garbage collector
* Heiko EiBfeldt : Missing verify_area check
* Alan Cox : Shutdown() bug
+ * Michael Deutschmann : release was writing to the socket
+ * structure after freeing it.
*
* 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).
+ * 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 ??)
+ * 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.
*/
/*
* Make sure the unix name is null-terminated.
*/
-
+
static inline void unix_mkname(struct sockaddr_un * sunaddr, unsigned long len)
{
if (len >= sizeof(*sunaddr))
* 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;
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.function=unix_destroy_timer;
add_timer(&sk->timer);
}
-
+
static void unix_destroy_socket(unix_socket *sk)
{
struct sk_buff *skb;
unix_remove_socket(sk);
-
+
+ sk->socket = NULL;
+
while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
{
if(sk->state==TCP_LISTEN)
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 */
+ /* osk will be destroyed when it gets to close or the timer fires */
}
else
{
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)
/*
* 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);
+ 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;
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) */
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 */
+ 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.
+ * 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;
}
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);
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);
*/
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);
else
return err;
}
-
+
return 0;
-
+
}
static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
* 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->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;
sk->state=TCP_SYN_SENT;
sock->state=SS_CONNECTING;
sti();
- other->data_ready(other,0); /* Wake up ! */
+ other->data_ready(other,0); /* Wake up ! */
}
-
-
+
+
/* Wait for an accept */
-
+
cli();
while(sk->state==TCP_SYN_SENT)
{
return -ERESTARTSYS;
}
}
-
+
/*
* Has the other end closed on us ?
*/
-
+
if(sk->state==TCP_CLOSE)
{
sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
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;
-
+ unix_socket *ska,*skb;
+
ska=a->data;
skb=b->data;
unix_socket *sk=sock->data;
unix_socket *newsk, *tsk;
struct sk_buff *skb;
-
+
if(sk->type!=SOCK_STREAM)
{
return -EOPNOTSUPP;
{
return -EINVAL;
}
-
+
newsk=newsock->data;
if(sk->protinfo.af_unix.name!=NULL)
{
return -ENOMEM;
strcpy(newsk->protinfo.af_unix.name, sk->protinfo.af_unix.name);
}
-
+
do
{
cli();
{
unix_socket *sk=sock->data;
struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
-
+
if(peer)
{
if(sk->protinfo.af_unix.other==NULL)
/*
* Support routines for struct cmsghdr handling
*/
-
+
static struct cmsghdr *unix_copyrights(void *userp, int len) /* AT&T ? */
{
struct cmsghdr *cm;
/*
* Return a header block
*/
-
+
static void unix_returnrights(void *userp, int len, struct cmsghdr *cm)
{
memcpy_tofs(userp, cm, len);
* 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);
num /= sizeof(int); /* 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];
+
+ fd = fdp[i];
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]->f_count++;
unix_inflight(fp[i]);
}
-
+
return num;
}
cmnum = (cmsg->cmsg_len-sizeof(struct cmsghdr)) / sizeof(int);
cmfptr = (int *)&cmsg->cmsg_data;
}
-
+
fdnum = *(int *)skb->h.filp;
fp = (struct file **)(skb->h.filp+sizeof(long));
{
unix_detach_fds(skb,NULL);
}
-
+
/*
* Attach the file descriptor array to an sk_buff
*/
/*
* 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;
if(flags&MSG_OOB)
return -EOPNOTSUPP;
-
+
if(flags) /* For now */ {
return -EINVAL;
}
-
+
if(sk->shutdown&SEND_SHUTDOWN)
{
send_sig(SIGPIPE,current,0);
return -EPIPE;
}
-
+
if(sunaddr!=NULL)
{
if(sock->type==SOCK_STREAM)
/*
* A control message has been attached.
*/
- if(msg->msg_control)
+ if(msg->msg_control)
{
- struct cmsghdr *cm=unix_copyrights(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 ||
msg->msg_controllen!=cm->cmsg_len)
{
kfree(cm);
- return -EINVAL;
+ return -EINVAL;
}
fpnum=unix_fd_copy(sk,cm,fp);
kfree(cm);
while(sent < len)
{
/*
- * Optimisation for the fact that under 0.01% of X messages typically
- * need breaking up.
+ * 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 */
/*
* Grab a buffer
*/
-
+
skb=sock_alloc_send_skb(sk,size,limit,nonblock, &err);
-
+
if(skb==NULL)
{
unix_fd_free(sk,fp,fpnum);
skb->sk=sk;
skb->free=1;
-
+
if(fpnum)
{
unix_attach_fds(fpnum,fp,skb);
/*
* Sleep until data has arrive. But check for races..
*/
-
+
static void unix_data_wait(unix_socket * sk)
{
cli();
if(flags&MSG_OOB)
return -EOPNOTSUPP;
-
+
if(addr_len)
*addr_len=0;
-
+
if(sk->err)
return sock_error(sk);
- if(msg->msg_control)
+ if(msg->msg_control)
{
- cm=unix_copyrights(msg->msg_control,
+ cm=unix_copyrights(msg->msg_control,
msg->msg_controllen);
if(msg->msg_controllen<sizeof(struct cmsghdr)
-#if 0
+#if 0
/* investigate this further -- Stevens example doesn't seem to care */
||
cm->cmsg_type!=SCM_RIGHTS ||
{
kfree(cm);
/* printk("recvmsg: Bad msg_control\n");*/
- return -EINVAL;
+ return -EINVAL;
}
}
-
+
down(&sk->protinfo.af_unix.readsem); /* Lock the socket */
while(ct--)
{
sp=iov->iov_base;
len=iov->iov_len;
iov++;
-
+
while(done<len)
{
if (copied && (flags & MSG_PEEK))
{
unix_socket *sk=(unix_socket *)sock->data;
unix_socket *other=sk->protinfo.af_unix.other;
-
+
mode++;
-
+
if(mode&SEND_SHUTDOWN)
{
sk->shutdown|=SEND_SHUTDOWN;
return 0;
}
-
+
static int unix_select(struct socket *sock, int sel_type, select_table *wait)
{
return datagram_select(sock->data,sel_type,wait);
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)
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",
len+=sprintf(buffer+len, " %s\n", s->protinfo.af_unix.name);
else
buffer[len++]='\n';
-
+
pos=begin+len;
if(pos<offset)
{
struct proto_ops unix_proto_ops = {
AF_UNIX,
-
+
unix_create,
unix_dup,
unix_release,