diff options
Diffstat (limited to 'fs/debugfs')
-rw-r--r-- | fs/debugfs/inode.c | 114 |
1 files changed, 96 insertions, 18 deletions
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index e9602d85c11d..08e28c9bb416 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c | |||
@@ -309,6 +309,31 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, | |||
309 | } | 309 | } |
310 | EXPORT_SYMBOL_GPL(debugfs_create_symlink); | 310 | EXPORT_SYMBOL_GPL(debugfs_create_symlink); |
311 | 311 | ||
312 | static void __debugfs_remove(struct dentry *dentry, struct dentry *parent) | ||
313 | { | ||
314 | int ret = 0; | ||
315 | |||
316 | if (debugfs_positive(dentry)) { | ||
317 | if (dentry->d_inode) { | ||
318 | dget(dentry); | ||
319 | switch (dentry->d_inode->i_mode & S_IFMT) { | ||
320 | case S_IFDIR: | ||
321 | ret = simple_rmdir(parent->d_inode, dentry); | ||
322 | break; | ||
323 | case S_IFLNK: | ||
324 | kfree(dentry->d_inode->i_private); | ||
325 | /* fall through */ | ||
326 | default: | ||
327 | simple_unlink(parent->d_inode, dentry); | ||
328 | break; | ||
329 | } | ||
330 | if (!ret) | ||
331 | d_delete(dentry); | ||
332 | dput(dentry); | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | |||
312 | /** | 337 | /** |
313 | * debugfs_remove - removes a file or directory from the debugfs filesystem | 338 | * debugfs_remove - removes a file or directory from the debugfs filesystem |
314 | * @dentry: a pointer to a the dentry of the file or directory to be | 339 | * @dentry: a pointer to a the dentry of the file or directory to be |
@@ -325,7 +350,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_symlink); | |||
325 | void debugfs_remove(struct dentry *dentry) | 350 | void debugfs_remove(struct dentry *dentry) |
326 | { | 351 | { |
327 | struct dentry *parent; | 352 | struct dentry *parent; |
328 | int ret = 0; | ||
329 | 353 | ||
330 | if (!dentry) | 354 | if (!dentry) |
331 | return; | 355 | return; |
@@ -335,29 +359,83 @@ void debugfs_remove(struct dentry *dentry) | |||
335 | return; | 359 | return; |
336 | 360 | ||
337 | mutex_lock(&parent->d_inode->i_mutex); | 361 | mutex_lock(&parent->d_inode->i_mutex); |
338 | if (debugfs_positive(dentry)) { | 362 | __debugfs_remove(dentry, parent); |
339 | if (dentry->d_inode) { | 363 | mutex_unlock(&parent->d_inode->i_mutex); |
340 | dget(dentry); | 364 | simple_release_fs(&debugfs_mount, &debugfs_mount_count); |
341 | switch (dentry->d_inode->i_mode & S_IFMT) { | 365 | } |
342 | case S_IFDIR: | 366 | EXPORT_SYMBOL_GPL(debugfs_remove); |
343 | ret = simple_rmdir(parent->d_inode, dentry); | 367 | |
344 | break; | 368 | /** |
345 | case S_IFLNK: | 369 | * debugfs_remove_recursive - recursively removes a directory |
346 | kfree(dentry->d_inode->i_private); | 370 | * @dentry: a pointer to a the dentry of the directory to be removed. |
347 | /* fall through */ | 371 | * |
348 | default: | 372 | * This function recursively removes a directory tree in debugfs that |
349 | simple_unlink(parent->d_inode, dentry); | 373 | * was previously created with a call to another debugfs function |
374 | * (like debugfs_create_file() or variants thereof.) | ||
375 | * | ||
376 | * This function is required to be called in order for the file to be | ||
377 | * removed, no automatic cleanup of files will happen when a module is | ||
378 | * removed, you are responsible here. | ||
379 | */ | ||
380 | void debugfs_remove_recursive(struct dentry *dentry) | ||
381 | { | ||
382 | struct dentry *child; | ||
383 | struct dentry *parent; | ||
384 | |||
385 | if (!dentry) | ||
386 | return; | ||
387 | |||
388 | parent = dentry->d_parent; | ||
389 | if (!parent || !parent->d_inode) | ||
390 | return; | ||
391 | |||
392 | parent = dentry; | ||
393 | mutex_lock(&parent->d_inode->i_mutex); | ||
394 | |||
395 | while (1) { | ||
396 | /* | ||
397 | * When all dentries under "parent" has been removed, | ||
398 | * walk up the tree until we reach our starting point. | ||
399 | */ | ||
400 | if (list_empty(&parent->d_subdirs)) { | ||
401 | mutex_unlock(&parent->d_inode->i_mutex); | ||
402 | if (parent == dentry) | ||
350 | break; | 403 | break; |
351 | } | 404 | parent = parent->d_parent; |
352 | if (!ret) | 405 | mutex_lock(&parent->d_inode->i_mutex); |
353 | d_delete(dentry); | 406 | } |
354 | dput(dentry); | 407 | child = list_entry(parent->d_subdirs.next, struct dentry, |
408 | d_u.d_child); | ||
409 | |||
410 | /* | ||
411 | * If "child" isn't empty, walk down the tree and | ||
412 | * remove all its descendants first. | ||
413 | */ | ||
414 | if (!list_empty(&child->d_subdirs)) { | ||
415 | mutex_unlock(&parent->d_inode->i_mutex); | ||
416 | parent = child; | ||
417 | mutex_lock(&parent->d_inode->i_mutex); | ||
418 | continue; | ||
355 | } | 419 | } |
420 | __debugfs_remove(child, parent); | ||
421 | if (parent->d_subdirs.next == &child->d_u.d_child) { | ||
422 | /* | ||
423 | * Avoid infinite loop if we fail to remove | ||
424 | * one dentry. | ||
425 | */ | ||
426 | mutex_unlock(&parent->d_inode->i_mutex); | ||
427 | break; | ||
428 | } | ||
429 | simple_release_fs(&debugfs_mount, &debugfs_mount_count); | ||
356 | } | 430 | } |
431 | |||
432 | parent = dentry->d_parent; | ||
433 | mutex_lock(&parent->d_inode->i_mutex); | ||
434 | __debugfs_remove(dentry, parent); | ||
357 | mutex_unlock(&parent->d_inode->i_mutex); | 435 | mutex_unlock(&parent->d_inode->i_mutex); |
358 | simple_release_fs(&debugfs_mount, &debugfs_mount_count); | 436 | simple_release_fs(&debugfs_mount, &debugfs_mount_count); |
359 | } | 437 | } |
360 | EXPORT_SYMBOL_GPL(debugfs_remove); | 438 | EXPORT_SYMBOL_GPL(debugfs_remove_recursive); |
361 | 439 | ||
362 | /** | 440 | /** |
363 | * debugfs_rename - rename a file/directory in the debugfs filesystem | 441 | * debugfs_rename - rename a file/directory in the debugfs filesystem |