summaryrefslogtreecommitdiffstats
path: root/fs/kernfs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-02-03 14:09:08 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-02-07 18:52:48 -0500
commit07c7530dd46728e25e938d0eb291f8085435c365 (patch)
treeaf31cc0ecb79794e1294b7781350afc45c1657d2 /fs/kernfs
parentce8b04aa6c9bdf211b921fdd18c040ea29516b97 (diff)
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: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/kernfs')
-rw-r--r--fs/kernfs/dir.c33
1 files changed, 30 insertions, 3 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 8c63ae1bccb6..bfbfb48f4ad8 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -654,22 +654,36 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry,
654{ 654{
655 struct kernfs_node *parent = dir->i_private; 655 struct kernfs_node *parent = dir->i_private;
656 struct kernfs_dir_ops *kdops = kernfs_root(parent)->dir_ops; 656 struct kernfs_dir_ops *kdops = kernfs_root(parent)->dir_ops;
657 int ret;
657 658
658 if (!kdops || !kdops->mkdir) 659 if (!kdops || !kdops->mkdir)
659 return -EPERM; 660 return -EPERM;
660 661
661 return kdops->mkdir(parent, dentry->d_name.name, mode); 662 if (!kernfs_get_active(parent))
663 return -ENODEV;
664
665 ret = kdops->mkdir(parent, dentry->d_name.name, mode);
666
667 kernfs_put_active(parent);
668 return ret;
662} 669}
663 670
664static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry) 671static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry)
665{ 672{
666 struct kernfs_node *kn = dentry->d_fsdata; 673 struct kernfs_node *kn = dentry->d_fsdata;
667 struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; 674 struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops;
675 int ret;
668 676
669 if (!kdops || !kdops->rmdir) 677 if (!kdops || !kdops->rmdir)
670 return -EPERM; 678 return -EPERM;
671 679
672 return kdops->rmdir(kn); 680 if (!kernfs_get_active(kn))
681 return -ENODEV;
682
683 ret = kdops->rmdir(kn);
684
685 kernfs_put_active(kn);
686 return ret;
673} 687}
674 688
675static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry, 689static 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 struct kernfs_node *kn = old_dentry->d_fsdata; 692 struct kernfs_node *kn = old_dentry->d_fsdata;
679 struct kernfs_node *new_parent = new_dir->i_private; 693 struct kernfs_node *new_parent = new_dir->i_private;
680 struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; 694 struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops;
695 int ret;
681 696
682 if (!kdops || !kdops->rename) 697 if (!kdops || !kdops->rename)
683 return -EPERM; 698 return -EPERM;
684 699
685 return kdops->rename(kn, new_parent, new_dentry->d_name.name); 700 if (!kernfs_get_active(kn))
701 return -ENODEV;
702
703 if (!kernfs_get_active(new_parent)) {
704 kernfs_put_active(kn);
705 return -ENODEV;
706 }
707
708 ret = kdops->rename(kn, new_parent, new_dentry->d_name.name);
709
710 kernfs_put_active(new_parent);
711 kernfs_put_active(kn);
712 return ret;
686} 713}
687 714
688const struct inode_operations kernfs_dir_iops = { 715const struct inode_operations kernfs_dir_iops = {