summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/debugfs/file.c157
-rw-r--r--fs/debugfs/inode.c70
-rw-r--r--fs/debugfs/internal.h6
3 files changed, 206 insertions, 27 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,
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 2905dd160575..136f269f01de 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -300,6 +300,37 @@ static struct dentry *end_creating(struct dentry *dentry)
300 return dentry; 300 return dentry;
301} 301}
302 302
303static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
304 struct dentry *parent, void *data,
305 const struct file_operations *proxy_fops,
306 const struct file_operations *real_fops)
307{
308 struct dentry *dentry;
309 struct inode *inode;
310
311 if (!(mode & S_IFMT))
312 mode |= S_IFREG;
313 BUG_ON(!S_ISREG(mode));
314 dentry = start_creating(name, parent);
315
316 if (IS_ERR(dentry))
317 return NULL;
318
319 inode = debugfs_get_inode(dentry->d_sb);
320 if (unlikely(!inode))
321 return failed_creating(dentry);
322
323 inode->i_mode = mode;
324 inode->i_private = data;
325
326 inode->i_fop = proxy_fops;
327 dentry->d_fsdata = (void *)real_fops;
328
329 d_instantiate(dentry, inode);
330 fsnotify_create(d_inode(dentry->d_parent), dentry);
331 return end_creating(dentry);
332}
333
303/** 334/**
304 * debugfs_create_file - create a file in the debugfs filesystem 335 * debugfs_create_file - create a file in the debugfs filesystem
305 * @name: a pointer to a string containing the name of the file to create. 336 * @name: a pointer to a string containing the name of the file to create.
@@ -330,33 +361,24 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode,
330 struct dentry *parent, void *data, 361 struct dentry *parent, void *data,
331 const struct file_operations *fops) 362 const struct file_operations *fops)
332{ 363{
333 struct dentry *dentry;
334 struct inode *inode;
335
336 if (!(mode & S_IFMT))
337 mode |= S_IFREG;
338 BUG_ON(!S_ISREG(mode));
339 dentry = start_creating(name, parent);
340
341 if (IS_ERR(dentry))
342 return NULL;
343
344 inode = debugfs_get_inode(dentry->d_sb);
345 if (unlikely(!inode))
346 return failed_creating(dentry);
347 364
348 inode->i_mode = mode; 365 return __debugfs_create_file(name, mode, parent, data,
349 inode->i_private = data; 366 fops ? &debugfs_full_proxy_file_operations :
367 &debugfs_noop_file_operations,
368 fops);
369}
370EXPORT_SYMBOL_GPL(debugfs_create_file);
350 371
351 inode->i_fop = fops ? &debugfs_open_proxy_file_operations 372struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
352 : &debugfs_noop_file_operations; 373 struct dentry *parent, void *data,
353 dentry->d_fsdata = (void *)fops; 374 const struct file_operations *fops)
375{
354 376
355 d_instantiate(dentry, inode); 377 return __debugfs_create_file(name, mode, parent, data,
356 fsnotify_create(d_inode(dentry->d_parent), dentry); 378 fops ? &debugfs_open_proxy_file_operations :
357 return end_creating(dentry); 379 &debugfs_noop_file_operations,
380 fops);
358} 381}
359EXPORT_SYMBOL_GPL(debugfs_create_file);
360 382
361/** 383/**
362 * debugfs_create_file_size - create a file in the debugfs filesystem 384 * debugfs_create_file_size - create a file in the debugfs filesystem
@@ -579,6 +601,7 @@ void debugfs_remove(struct dentry *dentry)
579 inode_unlock(d_inode(parent)); 601 inode_unlock(d_inode(parent));
580 if (!ret) 602 if (!ret)
581 simple_release_fs(&debugfs_mount, &debugfs_mount_count); 603 simple_release_fs(&debugfs_mount, &debugfs_mount_count);
604
582 synchronize_srcu(&debugfs_srcu); 605 synchronize_srcu(&debugfs_srcu);
583} 606}
584EXPORT_SYMBOL_GPL(debugfs_remove); 607EXPORT_SYMBOL_GPL(debugfs_remove);
@@ -657,6 +680,7 @@ void debugfs_remove_recursive(struct dentry *dentry)
657 if (!__debugfs_remove(child, parent)) 680 if (!__debugfs_remove(child, parent))
658 simple_release_fs(&debugfs_mount, &debugfs_mount_count); 681 simple_release_fs(&debugfs_mount, &debugfs_mount_count);
659 inode_unlock(d_inode(parent)); 682 inode_unlock(d_inode(parent));
683
660 synchronize_srcu(&debugfs_srcu); 684 synchronize_srcu(&debugfs_srcu);
661} 685}
662EXPORT_SYMBOL_GPL(debugfs_remove_recursive); 686EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h
index c7aaa5cb6685..bba52634b995 100644
--- a/fs/debugfs/internal.h
+++ b/fs/debugfs/internal.h
@@ -13,12 +13,14 @@
13#define _DEBUGFS_INTERNAL_H_ 13#define _DEBUGFS_INTERNAL_H_
14 14
15struct file_operations; 15struct file_operations;
16struct srcu_struct;
17 16
18/* declared over in file.c */ 17/* declared over in file.c */
19extern const struct file_operations debugfs_noop_file_operations; 18extern const struct file_operations debugfs_noop_file_operations;
20extern const struct file_operations debugfs_open_proxy_file_operations; 19extern const struct file_operations debugfs_open_proxy_file_operations;
20extern const struct file_operations debugfs_full_proxy_file_operations;
21 21
22extern struct srcu_struct debugfs_srcu; 22struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode,
23 struct dentry *parent, void *data,
24 const struct file_operations *fops);
23 25
24#endif /* _DEBUGFS_INTERNAL_H_ */ 26#endif /* _DEBUGFS_INTERNAL_H_ */