Commit 4768e9b18dc63719209c68920d4ae52dc49b6161

Authored by Louis Rilling
Committed by Mark Fasheh
1 parent dacdd0e047

[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>

Showing 2 changed files with 13 additions and 7 deletions Side-by-side Diff

... ... @@ -370,6 +370,9 @@
370 370 struct configfs_dirent *sd;
371 371 int ret;
372 372  
  373 + /* Mark that we're trying to drop the group */
  374 + parent_sd->s_type |= CONFIGFS_USET_DROPPING;
  375 +
373 376 ret = -EBUSY;
374 377 if (!list_empty(&parent_sd->s_links))
375 378 goto out;
... ... @@ -385,8 +388,6 @@
385 388 *wait_mutex = &sd->s_dentry->d_inode->i_mutex;
386 389 return -EAGAIN;
387 390 }
388   - /* Mark that we're trying to drop the group */
389   - sd->s_type |= CONFIGFS_USET_DROPPING;
390 391  
391 392 /*
392 393 * Yup, recursive. If there's a problem, blame
393 394  
... ... @@ -414,12 +415,11 @@
414 415 struct configfs_dirent *parent_sd = dentry->d_fsdata;
415 416 struct configfs_dirent *sd;
416 417  
417   - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
418   - if (sd->s_type & CONFIGFS_USET_DEFAULT) {
  418 + parent_sd->s_type &= ~CONFIGFS_USET_DROPPING;
  419 +
  420 + list_for_each_entry(sd, &parent_sd->s_children, s_sibling)
  421 + if (sd->s_type & CONFIGFS_USET_DEFAULT)
419 422 configfs_detach_rollback(sd->s_dentry);
420   - sd->s_type &= ~CONFIGFS_USET_DROPPING;
421   - }
422   - }
423 423 }
424 424  
425 425 static void detach_attrs(struct config_item * item)
fs/configfs/symlink.c
... ... @@ -78,6 +78,12 @@
78 78 if (sl) {
79 79 sl->sl_target = config_item_get(item);
80 80 spin_lock(&configfs_dirent_lock);
  81 + if (target_sd->s_type & CONFIGFS_USET_DROPPING) {
  82 + spin_unlock(&configfs_dirent_lock);
  83 + config_item_put(item);
  84 + kfree(sl);
  85 + return -ENOENT;
  86 + }
81 87 list_add(&sl->sl_list, &target_sd->s_links);
82 88 spin_unlock(&configfs_dirent_lock);
83 89 ret = configfs_create_link(sl, parent_item->ci_dentry,