diff options
author | Felix Fietkau <nbd@openwrt.org> | 2011-03-17 23:14:17 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2011-03-17 23:14:17 +0000 |
commit | 473ac1f87de132736c0768c8efc182b9c557476b (patch) | |
tree | c3103abfcef526055e18ca1a0b268c7a53432415 /target/linux/generic/patches-2.6.37 | |
parent | 4e2339b03210fe3434d0bff26672171a74f7cfe7 (diff) | |
download | master-31e0f0ae-473ac1f87de132736c0768c8efc182b9c557476b.tar.gz master-31e0f0ae-473ac1f87de132736c0768c8efc182b9c557476b.tar.bz2 master-31e0f0ae-473ac1f87de132736c0768c8efc182b9c557476b.zip |
kernel: fix an overlayfs deadlock on rmdir
SVN-Revision: 26215
Diffstat (limited to 'target/linux/generic/patches-2.6.37')
-rw-r--r-- | target/linux/generic/patches-2.6.37/212-overlayfs_fix_readdir_unlink_deadlock.patch | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/target/linux/generic/patches-2.6.37/212-overlayfs_fix_readdir_unlink_deadlock.patch b/target/linux/generic/patches-2.6.37/212-overlayfs_fix_readdir_unlink_deadlock.patch new file mode 100644 index 0000000000..6042b2968f --- /dev/null +++ b/target/linux/generic/patches-2.6.37/212-overlayfs_fix_readdir_unlink_deadlock.patch @@ -0,0 +1,76 @@ +--- a/fs/overlayfs/overlayfs.c ++++ b/fs/overlayfs/overlayfs.c +@@ -1686,37 +1686,56 @@ static int ovl_check_empty_dir(struct de + return err; + } + +-static int ovl_unlink_whiteout(void *buf, const char *name, int namelen, +- loff_t offset, u64 ino, unsigned int d_type) ++static int ovl_fill_links(void *buf, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) + { + struct ovl_readdir_data *rdd = buf; ++ struct ovl_cache_entry *p; + +- rdd->count++; +- /* check d_type to filter out "." and ".." */ +- if (d_type == DT_LNK) { +- struct dentry *dentry; ++ if (d_type != DT_LNK) ++ return 0; + +- dentry = lookup_one_len(name, rdd->dir, namelen); +- if (IS_ERR(dentry)) { +- rdd->err = PTR_ERR(dentry); +- } else { +- rdd->err = vfs_unlink(rdd->dir->d_inode, dentry); +- dput(dentry); +- } +- } ++ p = ovl_cache_entry_new(name, namelen, ino, d_type); ++ if (!p) ++ return -ENOMEM; + +- return rdd->err; ++ list_add(&p->l_node, rdd->list); ++ return 0; + } + + static int ovl_remove_whiteouts(struct dentry *dentry) + { + struct path upperpath; +- struct ovl_readdir_data rdd = { .list = NULL }; ++ LIST_HEAD(list); ++ struct ovl_readdir_data rdd = { .list = &list }; ++ struct ovl_cache_entry *p, *t; ++ int ret; + + ovl_path_upper(dentry, &upperpath); + rdd.dir = upperpath.dentry; + +- return ovl_dir_read(&upperpath, &rdd, ovl_unlink_whiteout); ++ ret = ovl_dir_read(&upperpath, &rdd, ovl_fill_links); ++ ++ mutex_lock(&rdd.dir->d_inode->i_mutex); ++ list_for_each_entry_safe(p, t, &list, l_node) { ++ struct dentry *dentry; ++ ++ if (!ret) { ++ dentry = lookup_one_len(p->name, rdd.dir, p->len); ++ if (IS_ERR(dentry)) { ++ ret = PTR_ERR(dentry); ++ } else { ++ ret = vfs_unlink(rdd.dir->d_inode, dentry); ++ dput(dentry); ++ } ++ } ++ ++ list_del(&p->l_node); ++ kfree(p); ++ } ++ mutex_unlock(&rdd.dir->d_inode->i_mutex); ++ ++ return ret; + } + + static int ovl_rmdir(struct inode *dir, struct dentry *dentry) |