From: nbd Date: Thu, 17 Mar 2011 23:14:17 +0000 (+0000) Subject: kernel: fix an overlayfs deadlock on rmdir X-Git-Url: https://git.rohieb.name/openwrt.git/commitdiff_plain/1938e7e2b9eab37ba42b341dfd9320f8afb73b64 kernel: fix an overlayfs deadlock on rmdir git-svn-id: svn://svn.openwrt.org/openwrt/trunk@26215 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- 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)