[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: Louis Rilling <louis.rilling@kerlabs.com>
Signed-off-by: Joel Becker <joel.becker@oracle.com>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
This commit is contained in:
Louis Rilling 2008-06-23 14:16:17 +02:00 committed by Mark Fasheh
parent dacdd0e047
commit 4768e9b18d
2 changed files with 13 additions and 7 deletions

View file

@ -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
*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)
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)

View file

@ -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,