提交 39d8c1b6 编写于 作者: D David S. Miller

[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: NDavid S. Miller <davem@davemloft.net>
上级 77d2ca35
...@@ -348,8 +348,8 @@ static struct dentry_operations sockfs_dentry_operations = { ...@@ -348,8 +348,8 @@ static struct dentry_operations sockfs_dentry_operations = {
/* /*
* Obtains the first available file descriptor and sets it up for use. * Obtains the first available file descriptor and sets it up for use.
* *
* This function creates file structure and maps it to fd space * These functions create file structures and maps them to fd space
* of current process. On success it returns file descriptor * of the current process. On success it returns file descriptor
* and file struct implicitly stored in sock->file. * and file struct implicitly stored in sock->file.
* Note that another thread may close file descriptor before we return * Note that another thread may close file descriptor before we return
* from this function. We use the fact that now we do not refer * from this function. We use the fact that now we do not refer
...@@ -362,52 +362,67 @@ static struct dentry_operations sockfs_dentry_operations = { ...@@ -362,52 +362,67 @@ static struct dentry_operations sockfs_dentry_operations = {
* but we take care of internal coherence yet. * 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; int fd;
struct qstr this;
char name[32];
/*
* Find a file descriptor suitable for return to the user.
*/
fd = get_unused_fd(); fd = get_unused_fd();
if (fd >= 0) { if (likely(fd >= 0)) {
struct file *file = get_empty_filp(); struct file *file = get_empty_filp();
if (!file) { *filep = file;
if (unlikely(!file)) {
put_unused_fd(fd); put_unused_fd(fd);
fd = -ENFILE; return -ENFILE;
goto out;
} }
} else
*filep = NULL;
return fd;
}
this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino); static int sock_attach_fd(struct socket *sock, struct file *file)
this.name = name; {
this.hash = SOCK_INODE(sock)->i_ino; 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); return 0;
if (!file->f_dentry) { }
put_filp(file);
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); put_unused_fd(fd);
fd = -ENOMEM; return err;
goto out;
} }
file->f_dentry->d_op = &sockfs_dentry_operations; fd_install(fd, newfile);
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);
} }
out:
return fd; return fd;
} }
...@@ -1349,7 +1364,8 @@ asmlinkage long sys_listen(int fd, int backlog) ...@@ -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) asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen)
{ {
struct socket *sock, *newsock; struct socket *sock, *newsock;
int err, len; struct file *newfile;
int err, len, newfd;
char address[MAX_SOCK_ADDR]; char address[MAX_SOCK_ADDR];
sock = sockfd_lookup(fd, &err); sock = sockfd_lookup(fd, &err);
...@@ -1369,28 +1385,38 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int _ ...@@ -1369,28 +1385,38 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int _
*/ */
__module_get(newsock->ops->owner); __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); err = security_socket_accept(sock, newsock);
if (err) if (err)
goto out_release; goto out_fd;
err = sock->ops->accept(sock, newsock, sock->file->f_flags); err = sock->ops->accept(sock, newsock, sock->file->f_flags);
if (err < 0) if (err < 0)
goto out_release; goto out_fd;
if (upeer_sockaddr) { if (upeer_sockaddr) {
if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) { if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) {
err = -ECONNABORTED; err = -ECONNABORTED;
goto out_release; goto out_fd;
} }
err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen); err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen);
if (err < 0) if (err < 0)
goto out_release; goto out_fd;
} }
/* File flags are not inherited via accept() unlike another OSes. */ /* File flags are not inherited via accept() unlike another OSes. */
if ((err = sock_map_fd(newsock)) < 0) fd_install(newfd, newfile);
goto out_release; err = newfd;
security_socket_post_accept(sock, newsock); security_socket_post_accept(sock, newsock);
...@@ -1398,6 +1424,9 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int _ ...@@ -1398,6 +1424,9 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int _
sockfd_put(sock); sockfd_put(sock);
out: out:
return err; return err;
out_fd:
put_filp(newfile);
put_unused_fd(newfd);
out_release: out_release:
sock_release(newsock); sock_release(newsock);
goto out_put; goto out_put;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册