patch-1.3.71 linux/net/unix/af_unix.c
Next file: linux/net/unix/garbage.c
Previous file: linux/net/unix/Makefile
Back to the patch index
Back to the overall index
- Lines: 432
- Date:
Mon Mar 4 09:16:41 1996
- Orig file:
v1.3.70/linux/net/unix/af_unix.c
- Orig date:
Fri Feb 23 13:54:44 1996
diff -u --recursive --new-file v1.3.70/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
@@ -21,6 +21,9 @@
* 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.
*
*
* Known differences from reference BSD that was tested:
@@ -273,7 +276,6 @@
static int unix_create(struct socket *sock, int protocol)
{
unix_socket *sk;
-/* printk("Unix create\n");*/
if(protocol && protocol != PF_UNIX)
return -EPROTONOSUPPORT;
sk=(unix_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
@@ -682,9 +684,194 @@
return 0;
}
-/* if msg->accrights != NULL, we have fds to pass.
- * Current implementation passes at most one fd.
+/*
+ * 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.
+ */
+
+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++)
+ {
+ if(fdp[i]<0||fdp[i]>=NR_OPEN)
+ return -EINVAL;
+ if(current->files->fd[fdp[i]]==NULL)
+ return -EBADF;
+ }
+
+ /*
+ * Make sure the garbage collector can cope.
+ */
+
+ if(unix_gc_free<num)
+ return -ENOBUFS;
+
+ for(i=0;i<=num;i++)
+ {
+ fp[i]=current->files->fd[fdp[i]];
+ fp[i]->f_count++;
+ unix_gc_add(sk, 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_gc_remove(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...
+ */
+
+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;
+ int cmnum;
+ struct file **fp;
+ struct file **ufp;
+ int *cmfptr=NULL; /* =NULL To keep gcc happy */
+ 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_gc_remove(fp[i]);
+ }
+ /*
+ * Dump those that don't
+ */
+ for(;i<fdnum;i++)
+ {
+ close_fp(fp[i]);
+ unix_gc_remove(fp[i]);
+ }
+ kfree(skb->h.filp);
+ skb->h.filp=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);
+ memcpy(skb->h.filp,&fpnum,sizeof(int));
+ 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;
@@ -694,8 +881,9 @@
struct sk_buff *skb;
int limit=0;
int sent=0;
- /* for passing fd, NULL indicates no fd */
- struct file *filp;
+ struct file *fp[UNIX_MAX_FD];
+ int fpnum=0;
+ int fp_attached=0;
if(sk->err)
return sock_error(sk);
@@ -723,27 +911,27 @@
return -ENOTCONN;
}
-
- /* see if we want to access rights (fd) -- at the moment,
- * we can pass none or 1 fd
- */
- filp = NULL;
- if(msg->msg_accrights) {
- /* then accrightslen is meaningful */
- if(msg->msg_accrightslen == sizeof(int)) {
- int fd;
-
- fd = get_user((int *) msg->msg_accrights);
- filp = file_from_fd(fd);
- if(!filp)
- return -EBADF;
- } else if(msg->msg_accrightslen != 0) {
- /* if we have accrights, we fail here */
- return -EINVAL;
- }
+ /*
+ * A control message has been attached.
+ */
+ if(msg->msg_accrights)
+ {
+ struct cmsghdr *cm=unix_copyrights(msg->msg_accrights,
+ msg->msg_accrightslen);
+ if(cm==NULL || msg->msg_accrightslen<sizeof(struct cmsghdr) ||
+ cm->cmsg_type!=SCM_RIGHTS ||
+ cm->cmsg_level!=SOL_SOCKET ||
+ msg->msg_accrightslen!=cm->cmsg_len)
+ {
+ kfree(cm);
+ return -EINVAL;
+ }
+ fpnum=unix_fd_copy(sk,cm,fp);
+ kfree(cm);
+ if(fpnum<0)
+ return fpnum;
}
- /* invariant -- flip points to a file to pass or NULL */
while(sent < len)
{
/*
@@ -756,7 +944,10 @@
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;
}
/*
@@ -778,6 +969,7 @@
if(skb==NULL)
{
+ unix_fd_free(sk,fp,fpnum);
if(sent)
{
sk->err=-err;
@@ -790,6 +982,14 @@
skb->sk=sk;
skb->free=1;
+ if(fpnum && !fp_attached)
+ {
+ fp_attached=1;
+ unix_attach_fds(fpnum,fp,skb);
+ fpnum=0;
+ }
+ else
+ skb->h.filp=NULL;
memcpy_fromiovec(skb_put(skb,size),msg->msg_iov, size);
cli();
@@ -823,16 +1023,9 @@
return err;
}
}
- /* at this point, we want to add an fd if we have one */
- skb->h.filp = filp;
- if (filp) {
- filp->f_count++;
- }
-
skb_queue_tail(&other->receive_queue, skb);
sti();
/* if we sent an fd, only do it once */
- filp = NULL;
other->data_ready(other,size);
sent+=size;
}
@@ -853,36 +1046,6 @@
}
sti();
}
-
-
-/*
- * return 0 if we can stick the fd, negative errno if we can't
- */
-static int stick_fd(struct file *filp, int *uaddr, int size)
-{
- int slot;
- int upper_bound;
-
- if (!uaddr || size < sizeof(int))
- return -EINVAL;
-
- upper_bound = current->rlim[RLIMIT_NOFILE].rlim_cur;
-
- if (upper_bound > NR_OPEN)
- upper_bound = NR_OPEN;
-
- for (slot = 0; slot < upper_bound; slot++) {
- if (current->files->fd[slot])
- continue;
- /* have an fd */
- current->files->fd[slot] = filp;
- FD_CLR(slot, ¤t->files->close_on_exec);
- /* need verify area here? */
- put_user(slot, uaddr);
- return 0;
- }
- return -EMFILE;
-}
static int unix_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
{
@@ -894,8 +1057,8 @@
int len;
int num;
struct iovec *iov=msg->msg_iov;
+ struct cmsghdr *cm=NULL;
int ct=msg->msg_iovlen;
- struct file *filp;
if(flags&MSG_OOB)
return -EOPNOTSUPP;
@@ -905,6 +1068,20 @@
if(sk->err)
return sock_error(sk);
+
+ if(msg->msg_accrights)
+ {
+ cm=unix_copyrights(msg->msg_accrights,
+ msg->msg_accrightslen);
+ if(msg->msg_accrightslen<sizeof(struct cmsghdr)||
+ cm->cmsg_type!=SCM_RIGHTS ||
+ cm->cmsg_level!=SOL_SOCKET ||
+ msg->msg_accrightslen!=cm->cmsg_len)
+ {
+ kfree(cm);
+ return -EINVAL;
+ }
+ }
down(&sk->protinfo.af_unix.readsem); /* Lock the socket */
while(ct--)
@@ -953,11 +1130,8 @@
num=min(skb->len,size-copied);
memcpy_tofs(sp, skb->data, num);
- if ((filp = skb->h.filp) != NULL) {
- skb->h.filp = NULL;
- if (stick_fd(filp, msg->msg_accrights, msg->msg_accrightslen) < 0)
- close_fp(filp);
- }
+ if (skb->h.filp!=NULL)
+ unix_detach_fds(skb,cm);
copied+=num;
done+=num;
@@ -970,12 +1144,14 @@
continue;
}
kfree_skb(skb, FREE_WRITE);
- if(sock->type==SOCK_DGRAM)
+ if(sock->type==SOCK_DGRAM || cm)
goto out;
}
}
out:
up(&sk->protinfo.af_unix.readsem);
+ if(cm)
+ unix_returnrights(msg->msg_accrights,msg->msg_accrightslen,cm);
return copied;
}
@@ -1118,7 +1294,7 @@
void unix_proto_init(struct net_proto *pro)
{
- printk("NET3: Unix domain sockets 0.10 BETA for Linux NET3.033.\n");
+ printk("NET3: Unix domain sockets 0.12 for Linux NET3.033.\n");
sock_register(unix_proto_ops.family, &unix_proto_ops);
#ifdef CONFIG_PROC_FS
proc_net_register(&(struct proc_dir_entry) {
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this