diff options
author | Louis Rilling <louis.rilling@kerlabs.com> | 2008-06-20 08:09:22 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2008-07-31 19:21:13 -0400 |
commit | 9a73d78cda750f12e25eb811878f2d9dbab1bc6e (patch) | |
tree | abdc6537d51ee4faeb41c452428f2bde72abc9c8 /fs/configfs/dir.c | |
parent | 4768e9b18dc63719209c68920d4ae52dc49b6161 (diff) |
[PATCH] configfs: Fix failing symlink() making rmdir() fail
On a similar pattern as mkdir() vs rmdir(), a failing symlink() may make rmdir()
fail for the symlink's parent and the symlink's target as well.
failing symlink() making target's rmdir() fail:
process 1: process 2:
symlink("A/S" -> "B")
allow_link()
create_link()
attach to "B" links list
rmdir("B")
detach_prep("B")
error because of new link
configfs_create_link("A", "S")
error (eg -ENOMEM)
failing symlink() making parent's rmdir() fail:
process 1: process 2:
symlink("A/D/S" -> "B")
allow_link()
create_link()
attach to "B" links list
configfs_create_link("A/D", "S")
make_dirent("A/D", "S")
rmdir("A")
detach_prep("A")
detach_prep("A/D")
error because of "S"
create("S")
error (eg -ENOMEM)
We cannot use the same solution as for mkdir() vs rmdir(), since rmdir() on the
target cannot wait on the i_mutex of the new symlink's parent without risking a
deadlock (with other symlink() or sys_rename()). Instead we define a global
mutex protecting all configfs symlinks attachment, so that rmdir() can avoid the
races above.
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>
Diffstat (limited to 'fs/configfs/dir.c')
-rw-r--r-- | fs/configfs/dir.c | 10 |
1 files changed, 10 insertions, 0 deletions
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index cb5ea44846af..4e228c80fe9f 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c | |||
@@ -1207,6 +1207,11 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1207 | return -EINVAL; | 1207 | return -EINVAL; |
1208 | } | 1208 | } |
1209 | 1209 | ||
1210 | /* | ||
1211 | * Ensure that no racing symlink() will make detach_prep() fail while | ||
1212 | * the new link is temporarily attached | ||
1213 | */ | ||
1214 | mutex_lock(&configfs_symlink_mutex); | ||
1210 | spin_lock(&configfs_dirent_lock); | 1215 | spin_lock(&configfs_dirent_lock); |
1211 | do { | 1216 | do { |
1212 | struct mutex *wait_mutex; | 1217 | struct mutex *wait_mutex; |
@@ -1215,6 +1220,7 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1215 | if (ret) { | 1220 | if (ret) { |
1216 | configfs_detach_rollback(dentry); | 1221 | configfs_detach_rollback(dentry); |
1217 | spin_unlock(&configfs_dirent_lock); | 1222 | spin_unlock(&configfs_dirent_lock); |
1223 | mutex_unlock(&configfs_symlink_mutex); | ||
1218 | if (ret != -EAGAIN) { | 1224 | if (ret != -EAGAIN) { |
1219 | config_item_put(parent_item); | 1225 | config_item_put(parent_item); |
1220 | return ret; | 1226 | return ret; |
@@ -1224,10 +1230,12 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1224 | mutex_lock(wait_mutex); | 1230 | mutex_lock(wait_mutex); |
1225 | mutex_unlock(wait_mutex); | 1231 | mutex_unlock(wait_mutex); |
1226 | 1232 | ||
1233 | mutex_lock(&configfs_symlink_mutex); | ||
1227 | spin_lock(&configfs_dirent_lock); | 1234 | spin_lock(&configfs_dirent_lock); |
1228 | } | 1235 | } |
1229 | } while (ret == -EAGAIN); | 1236 | } while (ret == -EAGAIN); |
1230 | spin_unlock(&configfs_dirent_lock); | 1237 | spin_unlock(&configfs_dirent_lock); |
1238 | mutex_unlock(&configfs_symlink_mutex); | ||
1231 | 1239 | ||
1232 | /* Get a working ref for the duration of this function */ | 1240 | /* Get a working ref for the duration of this function */ |
1233 | item = configfs_get_config_item(dentry); | 1241 | item = configfs_get_config_item(dentry); |
@@ -1517,11 +1525,13 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys) | |||
1517 | mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex, | 1525 | mutex_lock_nested(&configfs_sb->s_root->d_inode->i_mutex, |
1518 | I_MUTEX_PARENT); | 1526 | I_MUTEX_PARENT); |
1519 | mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD); | 1527 | mutex_lock_nested(&dentry->d_inode->i_mutex, I_MUTEX_CHILD); |
1528 | mutex_lock(&configfs_symlink_mutex); | ||
1520 | spin_lock(&configfs_dirent_lock); | 1529 | spin_lock(&configfs_dirent_lock); |
1521 | if (configfs_detach_prep(dentry, NULL)) { | 1530 | if (configfs_detach_prep(dentry, NULL)) { |
1522 | printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n"); | 1531 | printk(KERN_ERR "configfs: Tried to unregister non-empty subsystem!\n"); |
1523 | } | 1532 | } |
1524 | spin_unlock(&configfs_dirent_lock); | 1533 | spin_unlock(&configfs_dirent_lock); |
1534 | mutex_unlock(&configfs_symlink_mutex); | ||
1525 | configfs_detach_group(&group->cg_item); | 1535 | configfs_detach_group(&group->cg_item); |
1526 | dentry->d_inode->i_flags |= S_DEAD; | 1536 | dentry->d_inode->i_flags |= S_DEAD; |
1527 | mutex_unlock(&dentry->d_inode->i_mutex); | 1537 | mutex_unlock(&dentry->d_inode->i_mutex); |