From: Louis Rilling Date: Mon, 23 Jun 2008 12:16:17 +0000 (+0200) Subject: [PATCH] configfs: Fix symlink() to a removing item X-Git-Tag: v2.6.27-rc2~96^2~10 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=4768e9b18dc63719209c68920d4ae52dc49b6161;p=linux-2.6-omap-h63xx.git [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 Signed-off-by: Joel Becker Signed-off-by: Mark Fasheh --- diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 2495f23e33f..cb5ea44846a 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -370,6 +370,9 @@ static int configfs_detach_prep(struct dentry *dentry, struct mutex **wait_mutex struct configfs_dirent *sd; int ret; + /* Mark that we're trying to drop the group */ + parent_sd->s_type |= CONFIGFS_USET_DROPPING; + ret = -EBUSY; if (!list_empty(&parent_sd->s_links)) 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; 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 @@ -414,12 +415,11 @@ static void configfs_detach_rollback(struct dentry *dentry) struct configfs_dirent *parent_sd = dentry->d_fsdata; struct configfs_dirent *sd; - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (sd->s_type & CONFIGFS_USET_DEFAULT) { + parent_sd->s_type &= ~CONFIGFS_USET_DROPPING; + + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) + if (sd->s_type & CONFIGFS_USET_DEFAULT) configfs_detach_rollback(sd->s_dentry); - sd->s_type &= ~CONFIGFS_USET_DROPPING; - } - } } static void detach_attrs(struct config_item * item) diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index 0004d18c40a..c12801a12c3 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -78,6 +78,12 @@ static int create_link(struct config_item *parent_item, if (sl) { sl->sl_target = config_item_get(item); 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); spin_unlock(&configfs_dirent_lock); ret = configfs_create_link(sl, parent_item->ci_dentry,