diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-02-21 22:16:11 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-03-18 13:31:31 +0100 |
commit | bef6f8ab73dce6195f2abe09421f7a389bfcfa94 (patch) | |
tree | 76940eb1ba6f98e907c1a02dca166d6b19c05d2c /fs | |
parent | 962faa265c06f9bb4d4ec6d836fb71376557ad6d (diff) |
procfs: fix race between symlink removals and traversals
commit 7e0e953bb0cf649f93277ac8fb67ecbb7f7b04a9 upstream.
use_pde()/unuse_pde() in ->follow_link()/->put_link() resp.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/proc/generic.c | 12 | ||||
-rw-r--r-- | fs/proc/inode.c | 21 | ||||
-rw-r--r-- | fs/proc/internal.h | 1 |
3 files changed, 22 insertions, 12 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index b7f268eb5f45..2e2d9d5d78d9 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -19,7 +19,6 @@ #include <linux/mount.h> #include <linux/init.h> #include <linux/idr.h> -#include <linux/namei.h> #include <linux/bitops.h> #include <linux/spinlock.h> #include <linux/completion.h> @@ -162,17 +161,6 @@ void proc_free_inum(unsigned int inum) spin_unlock_irqrestore(&proc_inum_lock, flags); } -static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, __PDE_DATA(dentry->d_inode)); - return NULL; -} - -static const struct inode_operations proc_link_inode_operations = { - .readlink = generic_readlink, - .follow_link = proc_follow_link, -}; - /* * Don't create negative dentries here, return -ENOENT by hand * instead. diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 124fc43c7090..2f2815f3176e 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -23,6 +23,7 @@ #include <linux/slab.h> #include <linux/mount.h> #include <linux/magic.h> +#include <linux/namei.h> #include <asm/uaccess.h> @@ -401,6 +402,26 @@ static const struct file_operations proc_reg_file_ops_no_compat = { }; #endif +static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct proc_dir_entry *pde = PDE(dentry->d_inode); + if (unlikely(!use_pde(pde))) + return ERR_PTR(-EINVAL); + nd_set_link(nd, pde->data); + return pde; +} + +static void proc_put_link(struct dentry *dentry, struct nameidata *nd, void *p) +{ + unuse_pde(p); +} + +const struct inode_operations proc_link_inode_operations = { + .readlink = generic_readlink, + .follow_link = proc_follow_link, + .put_link = proc_put_link, +}; + struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) { struct inode *inode = new_inode_pseudo(sb); diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 651d09a11dde..8b8ca1db6316 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -202,6 +202,7 @@ struct pde_opener { int closing; struct completion *c; }; +extern const struct inode_operations proc_link_inode_operations; extern const struct inode_operations proc_pid_link_inode_operations; |