Commit 07c7530d authored by Tejun Heo's avatar Tejun Heo Committed by Greg Kroah-Hartman

kernfs: invoke dir_ops while holding active ref of the target node

kernfs_dir_ops are currently being invoked without any active
reference, which makes it tricky for the invoked operations to
determine whether the objects associated those nodes are safe to
access and will remain that way for the duration of such operations.

kernfs already has active_ref mechanism to deal with this which makes
the removal of a given node the synchronization point for gating the
file operations.  There's no reason for dir_ops to be any different.
Update the dir_ops handling so that active_ref is held while the
dir_ops are executing.  This guarantees that while a dir_ops is
executing the target nodes stay alive.

As kernfs_dir_ops doesn't have any in-kernel user at this point, this
doesn't affect anybody.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ce8b04aa
...@@ -654,22 +654,36 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry, ...@@ -654,22 +654,36 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry,
{ {
struct kernfs_node *parent = dir->i_private; struct kernfs_node *parent = dir->i_private;
struct kernfs_dir_ops *kdops = kernfs_root(parent)->dir_ops; struct kernfs_dir_ops *kdops = kernfs_root(parent)->dir_ops;
int ret;
if (!kdops || !kdops->mkdir) if (!kdops || !kdops->mkdir)
return -EPERM; return -EPERM;
return kdops->mkdir(parent, dentry->d_name.name, mode); if (!kernfs_get_active(parent))
return -ENODEV;
ret = kdops->mkdir(parent, dentry->d_name.name, mode);
kernfs_put_active(parent);
return ret;
} }
static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry) static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
{ {
struct kernfs_node *kn = dentry->d_fsdata; struct kernfs_node *kn = dentry->d_fsdata;
struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops;
int ret;
if (!kdops || !kdops->rmdir) if (!kdops || !kdops->rmdir)
return -EPERM; return -EPERM;
return kdops->rmdir(kn); if (!kernfs_get_active(kn))
return -ENODEV;
ret = kdops->rmdir(kn);
kernfs_put_active(kn);
return ret;
} }
static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry, static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,
...@@ -678,11 +692,24 @@ static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -678,11 +692,24 @@ static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,
struct kernfs_node *kn = old_dentry->d_fsdata; struct kernfs_node *kn = old_dentry->d_fsdata;
struct kernfs_node *new_parent = new_dir->i_private; struct kernfs_node *new_parent = new_dir->i_private;
struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops;
int ret;
if (!kdops || !kdops->rename) if (!kdops || !kdops->rename)
return -EPERM; return -EPERM;
return kdops->rename(kn, new_parent, new_dentry->d_name.name); if (!kernfs_get_active(kn))
return -ENODEV;
if (!kernfs_get_active(new_parent)) {
kernfs_put_active(kn);
return -ENODEV;
}
ret = kdops->rename(kn, new_parent, new_dentry->d_name.name);
kernfs_put_active(new_parent);
kernfs_put_active(kn);
return ret;
} }
const struct inode_operations kernfs_dir_iops = { const struct inode_operations kernfs_dir_iops = {
......
...@@ -111,7 +111,8 @@ struct kernfs_node { ...@@ -111,7 +111,8 @@ struct kernfs_node {
* kernfs_dir_ops may be specified on kernfs_create_root() to support * kernfs_dir_ops may be specified on kernfs_create_root() to support
* directory manipulation syscalls. These optional callbacks are invoked * directory manipulation syscalls. These optional callbacks are invoked
* on the matching syscalls and can perform any kernfs operations which * on the matching syscalls and can perform any kernfs operations which
* don't necessarily have to be the exact operation requested. * don't necessarily have to be the exact operation requested. An active
* reference is held for each kernfs_node parameter.
*/ */
struct kernfs_dir_ops { struct kernfs_dir_ops {
int (*mkdir)(struct kernfs_node *parent, const char *name, int (*mkdir)(struct kernfs_node *parent, const char *name,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment