summaryrefslogtreecommitdiffstats
path: root/fs/debugfs/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/debugfs/file.c')
-rw-r--r--fs/debugfs/file.c157
1 files changed, 155 insertions, 2 deletions
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 736ab3c988f2..6eb58a8ed03c 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -23,9 +23,12 @@
23#include <linux/atomic.h> 23#include <linux/atomic.h>
24#include <linux/device.h> 24#include <linux/device.h>
25#include <linux/srcu.h> 25#include <linux/srcu.h>
26#include <asm/poll.h>
26 27
27#include "internal.h" 28#include "internal.h"
28 29
30struct poll_table_struct;
31
29static ssize_t default_read_file(struct file *file, char __user *buf, 32static ssize_t default_read_file(struct file *file, char __user *buf,
30 size_t count, loff_t *ppos) 33 size_t count, loff_t *ppos)
31{ 34{
@@ -66,7 +69,7 @@ const struct file_operations debugfs_noop_file_operations = {
66 * debugfs_use_file_start() must be followed by a matching call 69 * debugfs_use_file_start() must be followed by a matching call
67 * to debugfs_use_file_finish(). 70 * to debugfs_use_file_finish().
68 */ 71 */
69static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) 72int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
70 __acquires(&debugfs_srcu) 73 __acquires(&debugfs_srcu)
71{ 74{
72 *srcu_idx = srcu_read_lock(&debugfs_srcu); 75 *srcu_idx = srcu_read_lock(&debugfs_srcu);
@@ -75,6 +78,7 @@ static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
75 return -EIO; 78 return -EIO;
76 return 0; 79 return 0;
77} 80}
81EXPORT_SYMBOL_GPL(debugfs_use_file_start);
78 82
79/** 83/**
80 * debugfs_use_file_finish - mark the end of file data access 84 * debugfs_use_file_finish - mark the end of file data access
@@ -85,10 +89,11 @@ static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx)
85 * debugfs_remove_recursive() blocked by a former call to 89 * debugfs_remove_recursive() blocked by a former call to
86 * debugfs_use_file_start() to proceed and return to its caller. 90 * debugfs_use_file_start() to proceed and return to its caller.
87 */ 91 */
88static void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) 92void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu)
89{ 93{
90 srcu_read_unlock(&debugfs_srcu, srcu_idx); 94 srcu_read_unlock(&debugfs_srcu, srcu_idx);
91} 95}
96EXPORT_SYMBOL_GPL(debugfs_use_file_finish);
92 97
93#define F_DENTRY(filp) ((filp)->f_path.dentry) 98#define F_DENTRY(filp) ((filp)->f_path.dentry)
94 99
@@ -131,6 +136,154 @@ const struct file_operations debugfs_open_proxy_file_operations = {
131 .open = open_proxy_open, 136 .open = open_proxy_open,
132}; 137};
133 138
139#define PROTO(args...) args
140#define ARGS(args...) args
141
142#define FULL_PROXY_FUNC(name, ret_type, filp, proto, args) \
143static ret_type full_proxy_ ## name(proto) \
144{ \
145 const struct dentry *dentry = F_DENTRY(filp); \
146 const struct file_operations *real_fops = \
147 REAL_FOPS_DEREF(dentry); \
148 int srcu_idx; \
149 ret_type r; \
150 \
151 r = debugfs_use_file_start(dentry, &srcu_idx); \
152 if (likely(!r)) \
153 r = real_fops->name(args); \
154 debugfs_use_file_finish(srcu_idx); \
155 return r; \
156}
157
158FULL_PROXY_FUNC(llseek, loff_t, filp,
159 PROTO(struct file *filp, loff_t offset, int whence),
160 ARGS(filp, offset, whence));
161
162FULL_PROXY_FUNC(read, ssize_t, filp,
163 PROTO(struct file *filp, char __user *buf, size_t size,
164 loff_t *ppos),
165 ARGS(filp, buf, size, ppos));
166
167FULL_PROXY_FUNC(write, ssize_t, filp,
168 PROTO(struct file *filp, const char __user *buf, size_t size,
169 loff_t *ppos),
170 ARGS(filp, buf, size, ppos));
171
172FULL_PROXY_FUNC(unlocked_ioctl, long, filp,
173 PROTO(struct file *filp, unsigned int cmd, unsigned long arg),
174 ARGS(filp, cmd, arg));
175
176static unsigned int full_proxy_poll(struct file *filp,
177 struct poll_table_struct *wait)
178{
179 const struct dentry *dentry = F_DENTRY(filp);
180 const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
181 int srcu_idx;
182 unsigned int r = 0;
183
184 if (debugfs_use_file_start(dentry, &srcu_idx)) {
185 debugfs_use_file_finish(srcu_idx);
186 return POLLHUP;
187 }
188
189 r = real_fops->poll(filp, wait);
190 debugfs_use_file_finish(srcu_idx);
191 return r;
192}
193
194static int full_proxy_release(struct inode *inode, struct file *filp)
195{
196 const struct dentry *dentry = F_DENTRY(filp);
197 const struct file_operations *real_fops = REAL_FOPS_DEREF(dentry);
198 const struct file_operations *proxy_fops = filp->f_op;
199 int r = 0;
200
201 /*
202 * We must not protect this against removal races here: the
203 * original releaser should be called unconditionally in order
204 * not to leak any resources. Releasers must not assume that
205 * ->i_private is still being meaningful here.
206 */
207 if (real_fops->release)
208 r = real_fops->release(inode, filp);
209
210 replace_fops(filp, d_inode(dentry)->i_fop);
211 kfree((void *)proxy_fops);
212 fops_put(real_fops);
213 return 0;
214}
215
216static void __full_proxy_fops_init(struct file_operations *proxy_fops,
217 const struct file_operations *real_fops)
218{
219 proxy_fops->release = full_proxy_release;
220 if (real_fops->llseek)
221 proxy_fops->llseek = full_proxy_llseek;
222 if (real_fops->read)
223 proxy_fops->read = full_proxy_read;
224 if (real_fops->write)
225 proxy_fops->write = full_proxy_write;
226 if (real_fops->poll)
227 proxy_fops->poll = full_proxy_poll;
228 if (real_fops->unlocked_ioctl)
229 proxy_fops->unlocked_ioctl = full_proxy_unlocked_ioctl;
230}
231
232static int full_proxy_open(struct inode *inode, struct file *filp)
233{
234 const struct dentry *dentry = F_DENTRY(filp);
235 const struct file_operations *real_fops = NULL;
236 struct file_operations *proxy_fops = NULL;
237 int srcu_idx, r;
238
239 r = debugfs_use_file_start(dentry, &srcu_idx);
240 if (r) {
241 r = -ENOENT;
242 goto out;
243 }
244
245 real_fops = REAL_FOPS_DEREF(dentry);
246 real_fops = fops_get(real_fops);
247 if (!real_fops) {
248 /* Huh? Module did not cleanup after itself at exit? */
249 WARN(1, "debugfs file owner did not clean up at exit: %pd",
250 dentry);
251 r = -ENXIO;
252 goto out;
253 }
254
255 proxy_fops = kzalloc(sizeof(*proxy_fops), GFP_KERNEL);
256 if (!proxy_fops) {
257 r = -ENOMEM;
258 goto free_proxy;
259 }
260 __full_proxy_fops_init(proxy_fops, real_fops);
261 replace_fops(filp, proxy_fops);
262
263 if (real_fops->open) {
264 r = real_fops->open(inode, filp);
265
266 if (filp->f_op != proxy_fops) {
267 /* No protection against file removal anymore. */
268 WARN(1, "debugfs file owner replaced proxy fops: %pd",
269 dentry);
270 goto free_proxy;
271 }
272 }
273
274 goto out;
275free_proxy:
276 kfree(proxy_fops);
277 fops_put(real_fops);
278out:
279 debugfs_use_file_finish(srcu_idx);
280 return r;
281}
282
283const struct file_operations debugfs_full_proxy_file_operations = {
284 .open = full_proxy_open,
285};
286
134static struct dentry *debugfs_create_mode(const char *name, umode_t mode, 287static struct dentry *debugfs_create_mode(const char *name, umode_t mode,
135 struct dentry *parent, void *value, 288 struct dentry *parent, void *value,
136 const struct file_operations *fops, 289 const struct file_operations *fops,