Commit 4768e9b1 authored by Louis Rilling's avatar Louis Rilling Committed by Mark Fasheh

[PATCH] configfs: Fix symlink() to a removing item

The rule for configfs symlinks is that symlinks always point to valid
config_items, and prevent the target from being removed. However,
configfs_symlink() only checks that it can grab a reference on the target item,
without ensuring that it remains alive until the symlink is correctly attached.

This patch makes configfs_symlink() fail whenever the target is being removed,
using the CONFIGFS_USET_DROPPING flag set by configfs_detach_prep() and
protected by configfs_dirent_lock.

This patch introduces a similar (weird?) behavior as with mkdir failures making
rmdir fail: if symlink() races with rmdir() of the parent directory (or its
youngest user-created ancestor if parent is a default group) or rmdir() of the
target directory, and then fails in configfs_create(), this can make the racing
rmdir() fail despite the concerned directory having no user-created entry (resp.
no symlink pointing to it or one of its default groups) in the end.
This behavior is fixed in later patches.
Signed-off-by: default avatarLouis Rilling <louis.rilling@kerlabs.com>
Signed-off-by: default avatarJoel Becker <joel.becker@oracle.com>
Signed-off-by: default avatarMark Fasheh <mfasheh@suse.com>
parent dacdd0e0
...@@ -370,6 +370,9 @@ static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex ...@@ -370,6 +370,9 @@ static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex
struct configfs_dirent *sd; struct configfs_dirent *sd;
int ret; int ret;
/* Mark that we're trying to drop the group */
parent_sd->s_type |= CONFIGFS_USET_DROPPING;
ret = -EBUSY; ret = -EBUSY;
if (!list_empty(&parent_sd->s_links)) if (!list_empty(&parent_sd->s_links))
goto out; goto out;
...@@ -385,8 +388,6 @@ static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex ...@@ -385,8 +388,6 @@ static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex
*wait_mutex = &sd->s_dentry->d_inode->i_mutex; *wait_mutex = &sd->s_dentry->d_inode->i_mutex;
return -EAGAIN; return -EAGAIN;
} }
/* Mark that we're trying to drop the group */
sd->s_type |= CONFIGFS_USET_DROPPING;
/* /*
* Yup, recursive. If there's a problem, blame * Yup, recursive. If there's a problem, blame
...@@ -414,12 +415,11 @@ static void configfs_detach_rollback(struct dentry *dentry) ...@@ -414,12 +415,11 @@ static void configfs_detach_rollback(struct dentry *dentry)
struct configfs_dirent *parent_sd = dentry->d_fsdata; struct configfs_dirent *parent_sd = dentry->d_fsdata;
struct configfs_dirent *sd; struct configfs_dirent *sd;
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { parent_sd->s_type &= ~CONFIGFS_USET_DROPPING;
if (sd->s_type & CONFIGFS_USET_DEFAULT) {
list_for_each_entry(sd, &parent_sd->s_children, s_sibling)
if (sd->s_type & CONFIGFS_USET_DEFAULT)
configfs_detach_rollback(sd->s_dentry); configfs_detach_rollback(sd->s_dentry);
sd->s_type &= ~CONFIGFS_USET_DROPPING;
}
}
} }
static void detach_attrs(struct config_item * item) static void detach_attrs(struct config_item * item)
......
...@@ -78,6 +78,12 @@ static int create_link(struct config_item *parent_item, ...@@ -78,6 +78,12 @@ static int create_link(struct config_item *parent_item,
if (sl) { if (sl) {
sl->sl_target = config_item_get(item); sl->sl_target = config_item_get(item);
spin_lock(&configfs_dirent_lock); spin_lock(&configfs_dirent_lock);
if (target_sd->s_type & CONFIGFS_USET_DROPPING) {
spin_unlock(&configfs_dirent_lock);
config_item_put(item);
kfree(sl);
return -ENOENT;
}
list_add(&sl->sl_list, &target_sd->s_links); list_add(&sl->sl_list, &target_sd->s_links);
spin_unlock(&configfs_dirent_lock); spin_unlock(&configfs_dirent_lock);
ret = configfs_create_link(sl, parent_item->ci_dentry, ret = configfs_create_link(sl, parent_item->ci_dentry,
......
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