diff options
Diffstat (limited to 'target/linux/generic/patches-2.6.38/101-overlayfs_fix_readdir_deadlock.patch')
-rw-r--r-- | target/linux/generic/patches-2.6.38/101-overlayfs_fix_readdir_deadlock.patch | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/target/linux/generic/patches-2.6.38/101-overlayfs_fix_readdir_deadlock.patch b/target/linux/generic/patches-2.6.38/101-overlayfs_fix_readdir_deadlock.patch new file mode 100644 index 0000000000..81ef4c084f --- /dev/null +++ b/target/linux/generic/patches-2.6.38/101-overlayfs_fix_readdir_deadlock.patch @@ -0,0 +1,124 @@ +--- a/fs/overlayfs/overlayfs.c ++++ b/fs/overlayfs/overlayfs.c +@@ -253,8 +253,7 @@ static struct ovl_cache_entry *ovl_cache + } + + static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, +- u64 ino, unsigned int d_type, +- bool is_whiteout) ++ u64 ino, unsigned int d_type) + { + struct ovl_cache_entry *p; + +@@ -267,7 +266,7 @@ static struct ovl_cache_entry *ovl_cache + p->len = len; + p->type = d_type; + p->ino = ino; +- p->is_whiteout = is_whiteout; ++ p->is_whiteout = false; + } + + return p; +@@ -275,7 +274,7 @@ static struct ovl_cache_entry *ovl_cache + + static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, + const char *name, int len, u64 ino, +- unsigned int d_type, bool is_whiteout) ++ unsigned int d_type) + { + struct rb_node **newp = &rdd->root->rb_node; + struct rb_node *parent = NULL; +@@ -296,11 +295,18 @@ static int ovl_cache_entry_add_rb(struct + return 0; + } + +- p = ovl_cache_entry_new(name, len, ino, d_type, is_whiteout); ++ p = ovl_cache_entry_new(name, len, ino, d_type); + if (p == NULL) + return -ENOMEM; + +- list_add_tail(&p->l_node, rdd->list); ++ /* ++ * Add links before other types to be able to quicky mark ++ * any whiteout entries ++ */ ++ if (d_type == DT_LNK) ++ list_add(&p->l_node, rdd->list); ++ else ++ list_add_tail(&p->l_node, rdd->list); + rb_link_node(&p->node, parent, newp); + rb_insert_color(&p->node, rdd->root); + +@@ -318,7 +324,7 @@ static int ovl_fill_lower(void *buf, con + if (p) { + list_move_tail(&p->l_node, rdd->middle); + } else { +- p = ovl_cache_entry_new(name, namelen, ino, d_type, false); ++ p = ovl_cache_entry_new(name, namelen, ino, d_type); + if (p == NULL) + rdd->err = -ENOMEM; + else +@@ -343,26 +349,9 @@ static int ovl_fill_upper(void *buf, con + loff_t offset, u64 ino, unsigned int d_type) + { + struct ovl_readdir_data *rdd = buf; +- bool is_whiteout = false; + + rdd->count++; +- if (d_type == DT_LNK) { +- struct dentry *dentry; +- +- dentry = lookup_one_len(name, rdd->dir, namelen); +- if (IS_ERR(dentry)) { +- rdd->err = PTR_ERR(dentry); +- goto out; +- } +- is_whiteout = ovl_is_whiteout(dentry); +- dput(dentry); +- } +- +- rdd->err = ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type, +- is_whiteout); +- +-out: +- return rdd->err; ++ return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type); + } + + static int ovl_dir_read(struct path *realpath, struct ovl_readdir_data *rdd, +@@ -428,6 +417,26 @@ static void ovl_dir_reset(struct file *f + } + } + ++static void ovl_dir_mark_whiteouts(struct ovl_readdir_data *rdd) ++{ ++ struct ovl_cache_entry *p; ++ struct dentry *dentry; ++ ++ mutex_lock(&rdd->dir->d_inode->i_mutex); ++ list_for_each_entry(p, rdd->list, l_node) { ++ if (p->type != DT_LNK) ++ break; ++ ++ dentry = lookup_one_len(p->name, rdd->dir, p->len); ++ if (IS_ERR(dentry)) ++ continue; ++ ++ p->is_whiteout = ovl_is_whiteout(dentry); ++ dput(dentry); ++ } ++ mutex_unlock(&rdd->dir->d_inode->i_mutex); ++} ++ + static int ovl_dir_read_merged(struct path *upperpath, struct path *lowerpath, + struct ovl_readdir_data *rdd) + { +@@ -441,6 +450,8 @@ static int ovl_dir_read_merged(struct pa + err = ovl_dir_read(upperpath, rdd, ovl_fill_upper); + if (err) + goto out; ++ ++ ovl_dir_mark_whiteouts(rdd); + } + /* + * Insert lowerpath entries before upperpath ones, this allows |