diff options
author | Davide Libenzi <davidel@xmailserver.org> | 2009-09-22 19:43:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 10:39:29 -0400 |
commit | 562787a5c32ccdf182de27793a83a9f2ee86cd77 (patch) | |
tree | 3308afd59d3b7449afa3d6a6cd624d06ce035e88 /fs/anon_inodes.c | |
parent | 515350b6fd041396f425180589e08812dd13615f (diff) |
anonfd: split interface into file creation and install
Split the anonfd interface into a bare file pointer creation one, and a
file pointer creation plus install one.
There are cases, like the usage of eventfds inside other kernel
interfaces, where the file pointer created by anonfd needs to be used
inside the initialization of other structures.
As it is right now, as soon as anon_inode_getfd() returns, the kenrle can
race with userspace closing the newly installed file descriptor.
This patch, while keeping the old anon_inode_getfd(), introduces a new
anon_inode_getfile() (whose services are reused in anon_inode_getfd())
that allows to split the file creation phase and the fd install one.
Once all the kernel structures are initialized, the code can call the
proper fd_install().
Gregory manifested the need for something like this inside KVM.
Signed-off-by: Davide Libenzi <davidel@xmailserver.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: James Morris <jmorris@namei.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Gregory Haskins <ghaskins@novell.com>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Acked-by: Roland Dreier <rolandd@cisco.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/anon_inodes.c')
-rw-r--r-- | fs/anon_inodes.c | 68 |
1 files changed, 51 insertions, 17 deletions
diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 47d4a01c5393..d11c51fc2a3f 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c | |||
@@ -77,28 +77,24 @@ static const struct address_space_operations anon_aops = { | |||
77 | * | 77 | * |
78 | * Creates a new file by hooking it on a single inode. This is useful for files | 78 | * Creates a new file by hooking it on a single inode. This is useful for files |
79 | * that do not need to have a full-fledged inode in order to operate correctly. | 79 | * that do not need to have a full-fledged inode in order to operate correctly. |
80 | * All the files created with anon_inode_getfd() will share a single inode, | 80 | * All the files created with anon_inode_getfile() will share a single inode, |
81 | * hence saving memory and avoiding code duplication for the file/inode/dentry | 81 | * hence saving memory and avoiding code duplication for the file/inode/dentry |
82 | * setup. Returns new descriptor or -error. | 82 | * setup. Returns the newly created file* or an error pointer. |
83 | */ | 83 | */ |
84 | int anon_inode_getfd(const char *name, const struct file_operations *fops, | 84 | struct file *anon_inode_getfile(const char *name, |
85 | void *priv, int flags) | 85 | const struct file_operations *fops, |
86 | void *priv, int flags) | ||
86 | { | 87 | { |
87 | struct qstr this; | 88 | struct qstr this; |
88 | struct dentry *dentry; | 89 | struct dentry *dentry; |
89 | struct file *file; | 90 | struct file *file; |
90 | int error, fd; | 91 | int error; |
91 | 92 | ||
92 | if (IS_ERR(anon_inode_inode)) | 93 | if (IS_ERR(anon_inode_inode)) |
93 | return -ENODEV; | 94 | return ERR_PTR(-ENODEV); |
94 | 95 | ||
95 | if (fops->owner && !try_module_get(fops->owner)) | 96 | if (fops->owner && !try_module_get(fops->owner)) |
96 | return -ENOENT; | 97 | return ERR_PTR(-ENOENT); |
97 | |||
98 | error = get_unused_fd_flags(flags); | ||
99 | if (error < 0) | ||
100 | goto err_module; | ||
101 | fd = error; | ||
102 | 98 | ||
103 | /* | 99 | /* |
104 | * Link the inode to a directory entry by creating a unique name | 100 | * Link the inode to a directory entry by creating a unique name |
@@ -110,7 +106,7 @@ int anon_inode_getfd(const char *name, const struct file_operations *fops, | |||
110 | this.hash = 0; | 106 | this.hash = 0; |
111 | dentry = d_alloc(anon_inode_mnt->mnt_sb->s_root, &this); | 107 | dentry = d_alloc(anon_inode_mnt->mnt_sb->s_root, &this); |
112 | if (!dentry) | 108 | if (!dentry) |
113 | goto err_put_unused_fd; | 109 | goto err_module; |
114 | 110 | ||
115 | /* | 111 | /* |
116 | * We know the anon_inode inode count is always greater than zero, | 112 | * We know the anon_inode inode count is always greater than zero, |
@@ -136,16 +132,54 @@ int anon_inode_getfd(const char *name, const struct file_operations *fops, | |||
136 | file->f_version = 0; | 132 | file->f_version = 0; |
137 | file->private_data = priv; | 133 | file->private_data = priv; |
138 | 134 | ||
135 | return file; | ||
136 | |||
137 | err_dput: | ||
138 | dput(dentry); | ||
139 | err_module: | ||
140 | module_put(fops->owner); | ||
141 | return ERR_PTR(error); | ||
142 | } | ||
143 | EXPORT_SYMBOL_GPL(anon_inode_getfile); | ||
144 | |||
145 | /** | ||
146 | * anon_inode_getfd - creates a new file instance by hooking it up to an | ||
147 | * anonymous inode, and a dentry that describe the "class" | ||
148 | * of the file | ||
149 | * | ||
150 | * @name: [in] name of the "class" of the new file | ||
151 | * @fops: [in] file operations for the new file | ||
152 | * @priv: [in] private data for the new file (will be file's private_data) | ||
153 | * @flags: [in] flags | ||
154 | * | ||
155 | * Creates a new file by hooking it on a single inode. This is useful for files | ||
156 | * that do not need to have a full-fledged inode in order to operate correctly. | ||
157 | * All the files created with anon_inode_getfd() will share a single inode, | ||
158 | * hence saving memory and avoiding code duplication for the file/inode/dentry | ||
159 | * setup. Returns new descriptor or an error code. | ||
160 | */ | ||
161 | int anon_inode_getfd(const char *name, const struct file_operations *fops, | ||
162 | void *priv, int flags) | ||
163 | { | ||
164 | int error, fd; | ||
165 | struct file *file; | ||
166 | |||
167 | error = get_unused_fd_flags(flags); | ||
168 | if (error < 0) | ||
169 | return error; | ||
170 | fd = error; | ||
171 | |||
172 | file = anon_inode_getfile(name, fops, priv, flags); | ||
173 | if (IS_ERR(file)) { | ||
174 | error = PTR_ERR(file); | ||
175 | goto err_put_unused_fd; | ||
176 | } | ||
139 | fd_install(fd, file); | 177 | fd_install(fd, file); |
140 | 178 | ||
141 | return fd; | 179 | return fd; |
142 | 180 | ||
143 | err_dput: | ||
144 | dput(dentry); | ||
145 | err_put_unused_fd: | 181 | err_put_unused_fd: |
146 | put_unused_fd(fd); | 182 | put_unused_fd(fd); |
147 | err_module: | ||
148 | module_put(fops->owner); | ||
149 | return error; | 183 | return error; |
150 | } | 184 | } |
151 | EXPORT_SYMBOL_GPL(anon_inode_getfd); | 185 | EXPORT_SYMBOL_GPL(anon_inode_getfd); |