summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2011-03-17 23:14:17 +0000
committernbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2011-03-17 23:14:17 +0000
commit1938e7e2b9eab37ba42b341dfd9320f8afb73b64 (patch)
treeea68f12720a97b1deac50d6ebdd29fb0a4b854ce
parentbf4ead177152d60b577521c2b2a3871bb769371b (diff)
kernel: fix an overlayfs deadlock on rmdir
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@26215 3c298f89-4303-0410-b956-a3cf2f4a3e73
-rw-r--r--target/linux/generic/patches-2.6.37/212-overlayfs_fix_readdir_unlink_deadlock.patch76
-rw-r--r--target/linux/generic/patches-2.6.38/211-overlayfs_fix_readdir_unlink_deadlock.patch76
2 files changed, 152 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 000000000..6042b2968
--- /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)
diff --git a/target/linux/generic/patches-2.6.38/211-overlayfs_fix_readdir_unlink_deadlock.patch b/target/linux/generic/patches-2.6.38/211-overlayfs_fix_readdir_unlink_deadlock.patch
new file mode 100644
index 000000000..6f4ceb4b7
--- /dev/null
+++ b/target/linux/generic/patches-2.6.38/211-overlayfs_fix_readdir_unlink_deadlock.patch
@@ -0,0 +1,76 @@
+--- a/fs/overlayfs/overlayfs.c
++++ b/fs/overlayfs/overlayfs.c
+@@ -1700,37 +1700,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)