diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/debugfs/file.c | 157 | ||||
-rw-r--r-- | fs/debugfs/inode.c | 70 | ||||
-rw-r--r-- | fs/debugfs/internal.h | 6 |
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 | ||
30 | struct poll_table_struct; | ||
31 | |||
29 | static ssize_t default_read_file(struct file *file, char __user *buf, | 32 | static 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 | */ |
69 | static int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) | 72 | int 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 | } |
81 | EXPORT_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 | */ |
88 | static void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) | 92 | void 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 | } |
96 | EXPORT_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) \ | ||
143 | static 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 | |||
158 | FULL_PROXY_FUNC(llseek, loff_t, filp, | ||
159 | PROTO(struct file *filp, loff_t offset, int whence), | ||
160 | ARGS(filp, offset, whence)); | ||
161 | |||
162 | FULL_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 | |||
167 | FULL_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 | |||
172 | FULL_PROXY_FUNC(unlocked_ioctl, long, filp, | ||
173 | PROTO(struct file *filp, unsigned int cmd, unsigned long arg), | ||
174 | ARGS(filp, cmd, arg)); | ||
175 | |||
176 | static 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 | |||
194 | static 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 | |||
216 | static 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 | |||
232 | static 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; | ||
275 | free_proxy: | ||
276 | kfree(proxy_fops); | ||
277 | fops_put(real_fops); | ||
278 | out: | ||
279 | debugfs_use_file_finish(srcu_idx); | ||
280 | return r; | ||
281 | } | ||
282 | |||
283 | const struct file_operations debugfs_full_proxy_file_operations = { | ||
284 | .open = full_proxy_open, | ||
285 | }; | ||
286 | |||
134 | static struct dentry *debugfs_create_mode(const char *name, umode_t mode, | 287 | static 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 | ||
303 | static 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 | } | ||
370 | EXPORT_SYMBOL_GPL(debugfs_create_file); | ||
350 | 371 | ||
351 | inode->i_fop = fops ? &debugfs_open_proxy_file_operations | 372 | struct 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 | } |
359 | EXPORT_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 | } |
584 | EXPORT_SYMBOL_GPL(debugfs_remove); | 607 | EXPORT_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 | } |
662 | EXPORT_SYMBOL_GPL(debugfs_remove_recursive); | 686 | EXPORT_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 | ||
15 | struct file_operations; | 15 | struct file_operations; |
16 | struct srcu_struct; | ||
17 | 16 | ||
18 | /* declared over in file.c */ | 17 | /* declared over in file.c */ |
19 | extern const struct file_operations debugfs_noop_file_operations; | 18 | extern const struct file_operations debugfs_noop_file_operations; |
20 | extern const struct file_operations debugfs_open_proxy_file_operations; | 19 | extern const struct file_operations debugfs_open_proxy_file_operations; |
20 | extern const struct file_operations debugfs_full_proxy_file_operations; | ||
21 | 21 | ||
22 | extern struct srcu_struct debugfs_srcu; | 22 | struct 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_ */ |