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, { struct kernfs_node *parent = dir->i_private; struct kernfs_dir_ops *kdops = kernfs_root(parent)->dir_ops; + int ret; if (!kdops || !kdops->mkdir) 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) { struct kernfs_node *kn = dentry->d_fsdata; struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; + int ret; if (!kdops || !kdops->rmdir) 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, @@ -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 *new_parent = new_dir->i_private; struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; + int ret; if (!kdops || !kdops->rename) 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 = { diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 02ac33435808..58a131ddc6a3 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -111,7 +111,8 @@ struct kernfs_node { * kernfs_dir_ops may be specified on kernfs_create_root() to support * directory manipulation syscalls. These optional callbacks are invoked * 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 { int (*mkdir)(struct kernfs_node *parent, const char *name,