aboutsummaryrefslogtreecommitdiffstats
path: root/fs/anon_inodes.c
diff options
context:
space:
mode:
authorDavide Libenzi <davidel@xmailserver.org>2009-09-22 19:43:57 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:29 -0400
commit562787a5c32ccdf182de27793a83a9f2ee86cd77 (patch)
tree3308afd59d3b7449afa3d6a6cd624d06ce035e88 /fs/anon_inodes.c
parent515350b6fd041396f425180589e08812dd13615f (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.c68
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 */
84int anon_inode_getfd(const char *name, const struct file_operations *fops, 84struct 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
137err_dput:
138 dput(dentry);
139err_module:
140 module_put(fops->owner);
141 return ERR_PTR(error);
142}
143EXPORT_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 */
161int 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
143err_dput:
144 dput(dentry);
145err_put_unused_fd: 181err_put_unused_fd:
146 put_unused_fd(fd); 182 put_unused_fd(fd);
147err_module:
148 module_put(fops->owner);
149 return error; 183 return error;
150} 184}
151EXPORT_SYMBOL_GPL(anon_inode_getfd); 185EXPORT_SYMBOL_GPL(anon_inode_getfd);