summaryrefslogtreecommitdiffstats
path: root/net/socket.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2006-03-20 17:13:49 -0800
committerDavid S. Miller <davem@davemloft.net>2006-03-20 17:13:49 -0800
commit39d8c1b6fbaeb8d6adec4a8c08365cc9eaca6ae4 (patch)
treee604ee025c6711caca1bf6ec2a406197bf5be843 /net/socket.c
parent77d2ca350018c507815f5d38a40ffb597eb9ae25 (diff)
downloadlinux-39d8c1b6fbaeb8d6adec4a8c08365cc9eaca6ae4.tar.bz2
[NET]: Do not lose accepted socket when -ENFILE/-EMFILE.
Try to allocate the struct file and an unused file descriptor before we try to pull a newly accepted socket out of the protocol layer. Based upon a patch by Prassana Meda. Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/socket.c')
-rw-r--r--net/socket.c113
1 files changed, 71 insertions, 42 deletions
diff --git a/net/socket.c b/net/socket.c
index 7e1bdef8b09e..74283610db15 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -348,8 +348,8 @@ static struct dentry_operations sockfs_dentry_operations = {
/*
* Obtains the first available file descriptor and sets it up for use.
*
- * This function creates file structure and maps it to fd space
- * of current process. On success it returns file descriptor
+ * These functions create file structures and maps them to fd space
+ * of the current process. On success it returns file descriptor
* and file struct implicitly stored in sock->file.
* Note that another thread may close file descriptor before we return
* from this function. We use the fact that now we do not refer
@@ -362,52 +362,67 @@ static struct dentry_operations sockfs_dentry_operations = {
* but we take care of internal coherence yet.
*/
-int sock_map_fd(struct socket *sock)
+static int sock_alloc_fd(struct file **filep)
{
int fd;
- struct qstr this;
- char name[32];
-
- /*
- * Find a file descriptor suitable for return to the user.
- */
fd = get_unused_fd();
- if (fd >= 0) {
+ if (likely(fd >= 0)) {
struct file *file = get_empty_filp();
- if (!file) {
+ *filep = file;
+ if (unlikely(!file)) {
put_unused_fd(fd);
- fd = -ENFILE;
- goto out;
+ return -ENFILE;
}
+ } else
+ *filep = NULL;
+ return fd;
+}
- this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino);
- this.name = name;
- this.hash = SOCK_INODE(sock)->i_ino;
+static int sock_attach_fd(struct socket *sock, struct file *file)
+{
+ struct qstr this;
+ char name[32];
+
+ this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino);
+ this.name = name;
+ this.hash = SOCK_INODE(sock)->i_ino;
+
+ file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this);
+ if (unlikely(!file->f_dentry))
+ return -ENOMEM;
+
+ file->f_dentry->d_op = &sockfs_dentry_operations;
+ d_add(file->f_dentry, SOCK_INODE(sock));
+ file->f_vfsmnt = mntget(sock_mnt);
+ file->f_mapping = file->f_dentry->d_inode->i_mapping;
+
+ sock->file = file;
+ file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops;
+ file->f_mode = FMODE_READ | FMODE_WRITE;
+ file->f_flags = O_RDWR;
+ file->f_pos = 0;
+ file->private_data = sock;
- file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this);
- if (!file->f_dentry) {
- put_filp(file);
+ return 0;
+}
+
+int sock_map_fd(struct socket *sock)
+{
+ struct file *newfile;
+ int fd = sock_alloc_fd(&newfile);
+
+ if (likely(fd >= 0)) {
+ int err = sock_attach_fd(sock, newfile);
+
+ if (unlikely(err < 0)) {
+ put_filp(newfile);
put_unused_fd(fd);
- fd = -ENOMEM;
- goto out;
+ return err;
}
- file->f_dentry->d_op = &sockfs_dentry_operations;
- d_add(file->f_dentry, SOCK_INODE(sock));
- file->f_vfsmnt = mntget(sock_mnt);
- file->f_mapping = file->f_dentry->d_inode->i_mapping;
-
- sock->file = file;
- file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops;
- file->f_mode = FMODE_READ | FMODE_WRITE;
- file->f_flags = O_RDWR;
- file->f_pos = 0;
- file->private_data = sock;
- fd_install(fd, file);
+ fd_install(fd, newfile);
}
-
-out:
return fd;
}
@@ -1349,7 +1364,8 @@ asmlinkage long sys_listen(int fd, int backlog)
asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen)
{
struct socket *sock, *newsock;
- int err, len;
+ struct file *newfile;
+ int err, len, newfd;
char address[MAX_SOCK_ADDR];
sock = sockfd_lookup(fd, &err);
@@ -1369,28 +1385,38 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int _
*/
__module_get(newsock->ops->owner);
+ newfd = sock_alloc_fd(&newfile);
+ if (unlikely(newfd < 0)) {
+ err = newfd;
+ goto out_release;
+ }
+
+ err = sock_attach_fd(newsock, newfile);
+ if (err < 0)
+ goto out_fd;
+
err = security_socket_accept(sock, newsock);
if (err)
- goto out_release;
+ goto out_fd;
err = sock->ops->accept(sock, newsock, sock->file->f_flags);
if (err < 0)
- goto out_release;
+ goto out_fd;
if (upeer_sockaddr) {
if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) {
err = -ECONNABORTED;
- goto out_release;
+ goto out_fd;
}
err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen);
if (err < 0)
- goto out_release;
+ goto out_fd;
}
/* File flags are not inherited via accept() unlike another OSes. */
- if ((err = sock_map_fd(newsock)) < 0)
- goto out_release;
+ fd_install(newfd, newfile);
+ err = newfd;
security_socket_post_accept(sock, newsock);
@@ -1398,6 +1424,9 @@ out_put:
sockfd_put(sock);
out:
return err;
+out_fd:
+ put_filp(newfile);
+ put_unused_fd(newfd);
out_release:
sock_release(newsock);
goto out_put;