Commit 996b7376 authored by Tejun Heo's avatar Tejun Heo Committed by Greg Kroah-Hartman

sysfs: flatten and fix sysfs_rename_dir() error handling

Error handling in sysfs_rename_dir() was broken.

* When lookup_one_len() fails, 0 is returned.

* If parent inode check fails, returns with inode mutex and rename
  rwsem held.

This patch fixes the above bugs and flattens error handling such that
it's more readable and easier to modify.
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent dfeb9fb0
...@@ -452,8 +452,9 @@ void sysfs_remove_dir(struct kobject * kobj) ...@@ -452,8 +452,9 @@ void sysfs_remove_dir(struct kobject * kobj)
int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent,
const char *new_name) const char *new_name)
{ {
int error = 0; int error;
struct dentry * new_dentry; struct dentry * new_dentry;
struct sysfs_dirent *sd, *parent_sd;
if (!new_parent) if (!new_parent)
return -EFAULT; return -EFAULT;
...@@ -462,40 +463,48 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, ...@@ -462,40 +463,48 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent,
mutex_lock(&new_parent->d_inode->i_mutex); mutex_lock(&new_parent->d_inode->i_mutex);
new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name));
if (!IS_ERR(new_dentry)) { if (IS_ERR(new_dentry)) {
/* By allowing two different directories with the error = PTR_ERR(new_dentry);
* same d_parent we allow this routine to move goto out_unlock;
* between different shadows of the same directory
*/
if (kobj->dentry->d_parent->d_inode != new_parent->d_inode)
return -EINVAL;
else if (new_dentry->d_parent->d_inode != new_parent->d_inode)
error = -EINVAL;
else if (new_dentry == kobj->dentry)
error = -EINVAL;
else if (!new_dentry->d_inode) {
error = kobject_set_name(kobj, "%s", new_name);
if (!error) {
struct sysfs_dirent *sd, *parent_sd;
d_add(new_dentry, NULL);
d_move(kobj->dentry, new_dentry);
sd = kobj->dentry->d_fsdata;
parent_sd = new_parent->d_fsdata;
list_del_init(&sd->s_sibling);
list_add(&sd->s_sibling, &parent_sd->s_children);
}
else
d_drop(new_dentry);
} else
error = -EEXIST;
dput(new_dentry);
} }
/* By allowing two different directories with the same
* d_parent we allow this routine to move between different
* shadows of the same directory
*/
error = -EINVAL;
if (kobj->dentry->d_parent->d_inode != new_parent->d_inode ||
new_dentry->d_parent->d_inode != new_parent->d_inode ||
new_dentry == kobj->dentry)
goto out_dput;
error = -EEXIST;
if (new_dentry->d_inode)
goto out_dput;
error = kobject_set_name(kobj, "%s", new_name);
if (error)
goto out_drop;
d_add(new_dentry, NULL);
d_move(kobj->dentry, new_dentry);
sd = kobj->dentry->d_fsdata;
parent_sd = new_parent->d_fsdata;
list_del_init(&sd->s_sibling);
list_add(&sd->s_sibling, &parent_sd->s_children);
error = 0;
goto out_unlock;
out_drop:
d_drop(new_dentry);
out_dput:
dput(new_dentry);
out_unlock:
mutex_unlock(&new_parent->d_inode->i_mutex); mutex_unlock(&new_parent->d_inode->i_mutex);
up_write(&sysfs_rename_sem); up_write(&sysfs_rename_sem);
return error; return error;
} }
......
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