patch-2.0.36 linux/net/socket.c

Next file: linux/net/unix/garbage.c
Previous file: linux/net/rose/rose_timer.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.35/linux/net/socket.c linux/net/socket.c
@@ -61,6 +61,7 @@
 #include <linux/stat.h>
 #include <linux/socket.h>
 #include <linux/fcntl.h>
+#include <linux/file.h>
 #include <linux/net.h>
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>
@@ -88,6 +89,7 @@
 		      int size);
 
 static void sock_close(struct inode *inode, struct file *file);
+static int sock_no_open(struct inode *inode, struct file *file);
 static int sock_select(struct inode *inode, struct file *file, int which, select_table *seltable);
 static int sock_ioctl(struct inode *inode, struct file *file,
 		      unsigned int cmd, unsigned long arg);
@@ -107,7 +109,7 @@
 	sock_select,
 	sock_ioctl,
 	NULL,			/* mmap */
-	NULL,			/* no special open code... */
+	sock_no_open,		/* special open code... */
 	sock_close,
 	NULL,			/* no fsync */
 	sock_fasync
@@ -216,22 +218,38 @@
  *	Go from a file number to its socket slot.
  */
 
-extern __inline struct socket *sockfd_lookup(int fd, struct file **pfile)
+extern __inline struct socket *sockfd_lookup(int fd, int *err)
 {
 	struct file *file;
 	struct inode *inode;
+	struct socket *sock;
 
-	if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd])) 
+	if (!(file = fget(fd)))
+	{
+		*err = -EBADF;
 		return NULL;
+	}
 
 	inode = file->f_inode;
-	if (!inode || !inode->i_sock)
-		return NULL;
 
-	if (pfile) 
-		*pfile = file;
+	if (!inode || !inode->i_sock || !(sock = socki_lookup(inode)))
+	{
+		*err = -ENOTSOCK;
+		fput(file, inode);
+		return NULL;
+	}
+	
+	if (sock->file != file) {
+		printk(KERN_ERR "socki_lookup: socket file changed!\n");
+		sock->file = file;
+	}
+	
+	return sock;
+}
 
-	return socki_lookup(inode);
+extern __inline__ void sockfd_put(struct socket *sock)
+{
+	fput(sock->file, sock->file->f_inode);
 }
 
 /*
@@ -264,6 +282,7 @@
 	sock->wait = &inode->i_wait;
 	sock->inode = inode;		/* "backlink": we could use pointer arithmetic instead */
 	sock->fasync_list = NULL;
+	sock->file = NULL;
 	sockets_in_use++;
 	return sock;
 }
@@ -279,6 +298,17 @@
 	sock_wake_async(peer, 1);
 }
 
+/*
+ *	In theory you can't get an open on this inode, but /proc provides
+ *	a back door. Remember to keep it shut otherwise you'll let the
+ *	creepy crawlies in.
+ */
+  
+static int sock_no_open(struct inode *inode, struct file *file)
+{
+	return -ENXIO;
+}
+
 void sock_release(struct socket *sock)
 {
 	int oldstate;
@@ -613,9 +643,10 @@
 
 	if ((fd1 = sys_socket(family, type, protocol)) < 0) 
 		return(fd1);
-	sock1 = sockfd_lookup(fd1, NULL);
+	sock1 = sockfd_lookup(fd1, &er);
 	if (!sock1->ops->socketpair) 
 	{
+		sockfd_put(sock1);
 		sys_close(fd1);
 		return(-EINVAL);
 	}
@@ -626,14 +657,17 @@
 
 	if ((fd2 = sys_socket(family, type, protocol)) < 0) 
 	{
+		sockfd_put(sock1);
 		sys_close(fd1);
 		return(-EINVAL);
 	}
 
-	sock2 = sockfd_lookup(fd2, NULL);
+	sock2 = sockfd_lookup(fd2, &er);
 	if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) 
 	{
+		sockfd_put(sock1);
 		sys_close(fd1);
+		sockfd_put(sock2);
 		sys_close(fd2);
 		return(i);
 	}
@@ -646,13 +680,17 @@
 	er=verify_area(VERIFY_WRITE, usockvec, sizeof(usockvec));
 	if(er)
 	{
+		sockfd_put(sock1);
 		sys_close(fd1);
+		sockfd_put(sock2);
 		sys_close(fd2);
 	 	return er;
 	}
 	put_user(fd1, &usockvec[0]);
 	put_user(fd2, &usockvec[1]);
 
+	sockfd_put(sock1);
+	sockfd_put(sock2);
 	return(0);
 }
 
@@ -668,24 +706,20 @@
 asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen)
 {
 	struct socket *sock;
-	int i;
 	char address[MAX_SOCK_ADDR];
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
   
 	if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0)
-	  	return err;
+	  	goto out;
   
-	if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0) 
-	{
-		return(i);
-	}
-	return(0);
+	if ((err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) > 0) 
+		err = 0;
+out:		
+	sockfd_put(sock);
+	return err;
 }
 
 
@@ -700,20 +734,23 @@
 	struct socket *sock;
 	int err=-EOPNOTSUPP;
 	
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 
 	if (sock->state != SS_UNCONNECTED) 
-		return(-EINVAL);
-
+	{
+		err=-EINVAL;
+		goto out;
+	}
+	
 	if (sock->ops && sock->ops->listen)
 	{
 		err=sock->ops->listen(sock, backlog);
 		if(!err)
 			sock->flags |= SO_ACCEPTCON;
 	}
+out:
+	sockfd_put(sock);
 	return(err);
 }
 
@@ -728,28 +765,23 @@
 
 asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
 {
-	struct file *file;
 	struct socket *sock, *newsock;
 	int i;
 	char address[MAX_SOCK_ADDR];
 	int len;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-  	if (!(sock = sockfd_lookup(fd, &file))) 
-		return(-ENOTSOCK);
-	if (sock->state != SS_UNCONNECTED) 
-	{
-		return(-EINVAL);
-	}
-	if (!(sock->flags & SO_ACCEPTCON)) 
+  	if (!(sock = sockfd_lookup(fd, &i))) 
+		return i;
+	if (sock->state != SS_UNCONNECTED || (!(sock->flags & SO_ACCEPTCON)))
 	{
+		sockfd_put(sock);
 		return(-EINVAL);
 	}
 
 	if (!(newsock = sock_alloc())) 
 	{
 		printk(KERN_WARNING "accept: no more sockets\n");
+		sockfd_put(sock);
 		return(-ENOSR);	/* Was: EAGAIN, but we are out of system
 				   resources! */
 	}
@@ -758,19 +790,22 @@
 	if ((i = sock->ops->dup(newsock, sock)) < 0) 
 	{
 		sock_release(newsock);
+		sockfd_put(sock);
 		return(i);
 	}
 
-	i = newsock->ops->accept(sock, newsock, file->f_flags);
+	i = newsock->ops->accept(sock, newsock, sock->file->f_flags);
 	if ( i < 0) 
 	{
 		sock_release(newsock);
+		sockfd_put(sock);
 		return(i);
 	}
 
 	if ((fd = get_fd(SOCK_INODE(newsock))) < 0) 
 	{
 		sock_release(newsock);
+		sockfd_put(sock);
 		return(-EINVAL);
 	}
 	newsock->file=current->files->fd[fd];
@@ -780,6 +815,7 @@
 		newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1);
 		move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
 	}
+	sockfd_put(sock);
 	return(fd);
 }
 
@@ -792,18 +828,14 @@
 asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen)
 {
 	struct socket *sock;
-	struct file *file;
-	int i;
 	char address[MAX_SOCK_ADDR];
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || (file=current->files->fd[fd]) == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, &file)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return(err);
 
 	if((err=move_addr_to_kernel(uservaddr,addrlen,address))<0)
-	  	return err;
+	  	goto out;
   
 	switch(sock->state) 
 	{
@@ -814,7 +846,8 @@
 			/* Socket is already connected */
 			if(sock->type == SOCK_DGRAM) /* Hack for now - move this all into the protocol */
 				break;
-			return -EISCONN;
+			err = -EISCONN;
+			goto out;
 		case SS_CONNECTING:
 			/* Not yet connected... we will check this. */
 		
@@ -825,14 +858,15 @@
 			 */
 			break;
 		default:
-			return(-EINVAL);
-	}
-	i = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, file->f_flags);
-	if (i < 0) 
-	{
-		return(i);
+			err = -EINVAL;
+			goto out;
 	}
-	return(0);
+	err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, sock->file->f_flags);
+	if (err > 0) 
+		err = 0;
+out:
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -847,17 +881,17 @@
 	int len;
 	int err;
 	
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 
 	err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 0);
 	if(err)
-		return err;
-	if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))<0)
-	  	return err;
-	return 0;
+		goto out;
+	if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))>0)
+		err = 0;
+out:
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -872,17 +906,17 @@
 	int len;
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 
 	err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 1);
 	if(err)
-	  	return err;
-	if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))<0)
-	  	return err;
-	return 0;
+	  	goto out;
+	if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))>0)
+		err = 0;
+out:
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -893,29 +927,28 @@
 asmlinkage int sys_send(int fd, void * buff, int len, unsigned flags)
 {
 	struct socket *sock;
-	struct file *file;
 	int err;
 	struct msghdr msg;
 	struct iovec iov;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
-
 	if(len<0)
 		return -EINVAL;
 	err=verify_area(VERIFY_READ, buff, len);
 	if(err)
 		return err;
 		
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
+		
 	iov.iov_base=buff;
 	iov.iov_len=len;
 	msg.msg_name=NULL;
 	msg.msg_iov=&iov;
 	msg.msg_iovlen=1;
 	msg.msg_control=NULL;
-	return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), flags));
+	err=sock->ops->sendmsg(sock, &msg, len, (sock->file->f_flags & O_NONBLOCK), flags);
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -928,22 +961,18 @@
 	   struct sockaddr *addr, int addr_len)
 {
 	struct socket *sock;
-	struct file *file;
 	char address[MAX_SOCK_ADDR];
 	int err;
 	struct msghdr msg;
 	struct iovec iov;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-
 	if(len<0)
 		return -EINVAL;
 	err=verify_area(VERIFY_READ,buff,len);
 	if(err)
 	  	return err;
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 
 	iov.iov_base=buff;
 	iov.iov_len=len;
@@ -955,13 +984,17 @@
 	if (addr && addr_len) {
 		err=move_addr_to_kernel(addr,addr_len,address);
 		if (err < 0)
+		{
+			sockfd_put(sock);
 			return err;
+		}
 		msg.msg_name=address;
 		msg.msg_namelen=addr_len;
 	}
 	  	
-	return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK),
-		flags));
+	err=sock->ops->sendmsg(sock, &msg, len, (sock->file->f_flags & O_NONBLOCK), flags);
+	sockfd_put(sock);
+	return err;
 }
 
 
@@ -974,15 +1007,8 @@
 	struct iovec iov;
 	struct msghdr msg;
 	struct socket *sock;
-	struct file *file;
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
-		
 	if(size<0)
 		return -EINVAL;
 	if(size==0)
@@ -990,6 +1016,8 @@
 	err=verify_area(VERIFY_WRITE, ubuf, size);
 	if(err)
 		return err;
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 		
 	msg.msg_name=NULL;
 	msg.msg_iov=&iov;
@@ -998,7 +1026,9 @@
 	iov.iov_base=ubuf;
 	iov.iov_len=size;
 
-	return(sock->ops->recvmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK), flags,&msg.msg_namelen));
+	err=sock->ops->recvmsg(sock, &msg, size,(sock->file->f_flags & O_NONBLOCK), flags,&msg.msg_namelen);
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -1011,24 +1041,20 @@
 	     struct sockaddr *addr, int *addr_len)
 {
 	struct socket *sock;
-	struct file *file;
 	struct iovec iov;
 	struct msghdr msg;
 	char address[MAX_SOCK_ADDR];
 	int err;
 	int alen;
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-	  	return(-ENOTSOCK);
 	if(size<0)
 		return -EINVAL;
 	if(size==0)
 		return 0;
-
 	err=verify_area(VERIFY_WRITE,ubuf,size);
 	if(err)
 	  	return err;
+	if (!(sock = sockfd_lookup(fd, &err))) 
+	  	return err;
   
   	msg.msg_control=NULL;
   	msg.msg_iovlen=1;
@@ -1037,9 +1063,11 @@
   	iov.iov_base=ubuf;
   	msg.msg_name=address;
   	msg.msg_namelen=MAX_SOCK_ADDR;
-	size=sock->ops->recvmsg(sock, &msg, size, (file->f_flags & O_NONBLOCK),
+	size=sock->ops->recvmsg(sock, &msg, size, (sock->file->f_flags & O_NONBLOCK),
 		     flags, &alen);
 
+	sockfd_put(sock);
+
 	if(size<0)
 	 	return size;
 	if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0)
@@ -1056,14 +1084,14 @@
 asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen)
 {
 	struct socket *sock;
-	struct file *file;
+	int err;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 
-	return(sock->ops->setsockopt(sock, level, optname, optval, optlen));
+	err=sock->ops->setsockopt(sock, level, optname, optval, optlen);
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -1074,16 +1102,19 @@
 asmlinkage int sys_getsockopt(int fd, int level, int optname, char *optval, int *optlen)
 {
 	struct socket *sock;
-	struct file *file;
+	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-	    
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
+		
 	if (!sock->ops->getsockopt) 
+	{
+		sockfd_put(sock);
 		return(0);
-	return(sock->ops->getsockopt(sock, level, optname, optval, optlen));
+	}
+	err=sock->ops->getsockopt(sock, level, optname, optval, optlen);
+	sockfd_put(sock);
+	return err;
 }
 
 
@@ -1094,14 +1125,13 @@
 asmlinkage int sys_shutdown(int fd, int how)
 {
 	struct socket *sock;
-	struct file *file;
-
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	int err;
 
-	return(sock->ops->shutdown(sock, how));
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
+	err=sock->ops->shutdown(sock, how);
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -1111,39 +1141,43 @@
 asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned int flags)
 {
 	struct socket *sock;
-	struct file *file;
 	char address[MAX_SOCK_ADDR];
 	struct iovec iov[UIO_MAXIOV];
 	struct msghdr msg_sys;
 	int err;
 	int total_len;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-	
-	if(sock->ops->sendmsg==NULL)
-		return -EOPNOTSUPP;
-
-
 	err=verify_area(VERIFY_READ, msg,sizeof(struct msghdr));
 	if(err)
 		return err;
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
+	if(sock->ops->sendmsg==NULL)
+	{
+		err = -EOPNOTSUPP;
+		goto out;
+	}
 
 	memcpy_fromfs(&msg_sys,msg,sizeof(struct msghdr));
 
 	/* do not move before msg_sys is valid */
 	if(msg_sys.msg_iovlen>UIO_MAXIOV)
-		return -EINVAL;
+	{
+		err = -EINVAL;
+		goto out;
+	}
 
 	/* This will also move the address data into kernel space */
 	err = verify_iovec(&msg_sys, iov, address, VERIFY_READ);
 	if (err < 0)
-		return err;
+		goto out;
+
 	total_len=err;
 
-	return sock->ops->sendmsg(sock, &msg_sys, total_len, (file->f_flags&O_NONBLOCK), flags);
+	err=sock->ops->sendmsg(sock, &msg_sys, total_len, (sock->file->f_flags&O_NONBLOCK), flags);
+out:
+	sockfd_put(sock);
+	return err;	
 }
 
 /*
@@ -1153,7 +1187,6 @@
 asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags)
 {
 	struct socket *sock;
-	struct file *file;
 	struct iovec iov[UIO_MAXIOV];
 	struct msghdr msg_sys;
 	int err;
@@ -1168,11 +1201,6 @@
 	struct sockaddr *uaddr;
 	int *uaddr_len;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-	
 	err=verify_area(VERIFY_READ, msg,sizeof(struct msghdr));
 	if(err)
 		return err;
@@ -1180,6 +1208,10 @@
 	if(msg_sys.msg_iovlen>UIO_MAXIOV)
 		return -EINVAL;
 
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
+	
+
 	/*
 	 * save the user-mode address (verify_iovec will change the
 	 * kernel msghdr to use the kernel address space)
@@ -1188,22 +1220,31 @@
 	uaddr_len = &msg->msg_namelen;
 	err=verify_iovec(&msg_sys,iov,addr, VERIFY_WRITE);
 	if(err<0)
-		return err;
+		goto out;
 
 	total_len=err;
 	
 	if(sock->ops->recvmsg==NULL)
-		return -EOPNOTSUPP;
-	len=sock->ops->recvmsg(sock, &msg_sys, total_len, (file->f_flags&O_NONBLOCK), flags, &addr_len);
-	if(len<0)
-		return len;
+	{
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+	err=sock->ops->recvmsg(sock, &msg_sys, total_len, (sock->file->f_flags&O_NONBLOCK), flags, &addr_len);
+	if(err<0)
+		goto out;
+	len=err;
 
 	if (uaddr != NULL) {
 		err = move_addr_to_user(addr, addr_len, uaddr, uaddr_len);
 		if (err)
-			return err;
+			goto out;
 	}
+	sockfd_put(sock);
 	return len;
+	
+out:
+	sockfd_put(sock);
+	return err;
 }
 
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov