summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2015-02-21 22:05:11 -0500
committerBen Hutchings <ben@decadent.org.uk>2015-05-09 23:16:19 +0100
commit915f4f86ddc49484217a0e0e16e8a25e3c1ec856 (patch)
tree2294ac4c2aef6a419e574549f9834a71a885d3dd /fs
parentaafdaa379ae3fb58acefa092b8edc3a64c929bb9 (diff)
debugfs: leave freeing a symlink body until inode eviction
commit 0db59e59299f0b67450c5db21f7f316c8fb04e84 upstream. As it is, we have debugfs_remove() racing with symlink traversals. Supply ->evict_inode() and do freeing there - inode will remain pinned until we are done with the symlink body. And rip the idiocy with checking if dentry is positive right after we'd verified debugfs_positive(), which is a stronger check... Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> [bwh: Backported to 3.2: - Plumb in debugfs_super_operations, which we didn't previously define - Call truncate_inode_pages() instead of truncate_inode_pages_final() - Call end_writeback() instead of clear_inode()] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/debugfs/inode.c46
1 files changed, 28 insertions, 18 deletions
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 6ac0893ca9fe..a15f1e220a76 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -125,11 +125,30 @@ static inline int debugfs_positive(struct dentry *dentry)
return dentry->d_inode && !d_unhashed(dentry);
}
+static void debugfs_evict_inode(struct inode *inode)
+{
+ truncate_inode_pages(&inode->i_data, 0);
+ end_writeback(inode);
+ if (S_ISLNK(inode->i_mode))
+ kfree(inode->i_private);
+}
+
+static const struct super_operations debugfs_super_operations = {
+ .evict_inode = debugfs_evict_inode,
+};
+
static int debug_fill_super(struct super_block *sb, void *data, int silent)
{
static struct tree_descr debug_files[] = {{""}};
+ int err;
- return simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
+ err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
+ if (err)
+ return err;
+
+ sb->s_op = &debugfs_super_operations;
+
+ return 0;
}
static struct dentry *debug_mount(struct file_system_type *fs_type,
@@ -312,23 +331,14 @@ static int __debugfs_remove(struct dentry *dentry, struct dentry *parent)
int ret = 0;
if (debugfs_positive(dentry)) {
- if (dentry->d_inode) {
- dget(dentry);
- switch (dentry->d_inode->i_mode & S_IFMT) {
- case S_IFDIR:
- ret = simple_rmdir(parent->d_inode, dentry);
- break;
- case S_IFLNK:
- kfree(dentry->d_inode->i_private);
- /* fall through */
- default:
- simple_unlink(parent->d_inode, dentry);
- break;
- }
- if (!ret)
- d_delete(dentry);
- dput(dentry);
- }
+ dget(dentry);
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ ret = simple_rmdir(parent->d_inode, dentry);
+ else
+ simple_unlink(parent->d_inode, dentry);
+ if (!ret)
+ d_delete(dentry);
+ dput(dentry);
}
return ret;
}