summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2015-02-22 06:05:11 +0300
committerAl Viro <viro@zeniv.linux.org.uk>2015-02-22 19:38:43 +0300
commit0db59e59299f0b67450c5db21f7f316c8fb04e84 (patch)
tree4354f66cf8b6ec6c36968ba63ea9e037d0a6dcbd
parentdca111782c9955a3d439d88fecc8a81cb1df4719 (diff)
downloadlinux-0db59e59299f0b67450c5db21f7f316c8fb04e84.tar.xz
debugfs: leave freeing a symlink body until inode eviction
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... Cc: stable@vger.kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/debugfs/inode.c34
1 files changed, 17 insertions, 17 deletions
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 90933645298c..96400ab42d13 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -169,10 +169,19 @@ static int debugfs_show_options(struct seq_file *m, struct dentry *root)
return 0;
}
+static void debugfs_evict_inode(struct inode *inode)
+{
+ truncate_inode_pages_final(&inode->i_data);
+ clear_inode(inode);
+ if (S_ISLNK(inode->i_mode))
+ kfree(inode->i_private);
+}
+
static const struct super_operations debugfs_super_operations = {
.statfs = simple_statfs,
.remount_fs = debugfs_remount,
.show_options = debugfs_show_options,
+ .evict_inode = debugfs_evict_inode,
};
static struct vfsmount *debugfs_automount(struct path *path)
@@ -511,23 +520,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;
}