1 Patch by: Miklos Szeredi <miklos@szeredi.hu>
3 Some filesystems (e.g. jffs2) lock the same resources for both readdir
4 and lookup, leading to a deadlock in ovl_cache_entry_new, which is called
5 from the filldir, and calls lookup itself.
7 --- a/fs/overlayfs/readdir.c
8 +++ b/fs/overlayfs/readdir.c
9 @@ -23,6 +23,7 @@ struct ovl_cache_entry {
11 struct list_head l_node;
13 + struct ovl_cache_entry *next_maybe_whiteout;
17 @@ -39,7 +40,7 @@ struct ovl_readdir_data {
19 struct list_head *list;
20 struct list_head middle;
22 + struct ovl_cache_entry *first_maybe_whiteout;
26 @@ -79,7 +80,7 @@ static struct ovl_cache_entry *ovl_cache
30 -static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
31 +static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
32 const char *name, int len,
33 u64 ino, unsigned int d_type)
35 @@ -98,29 +99,8 @@ static struct ovl_cache_entry *ovl_cache
36 p->is_whiteout = false;
38 if (d_type == DT_CHR) {
39 - struct dentry *dentry;
40 - const struct cred *old_cred;
41 - struct cred *override_cred;
43 - override_cred = prepare_creds();
44 - if (!override_cred) {
50 - * CAP_DAC_OVERRIDE for lookup
52 - cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
53 - old_cred = override_creds(override_cred);
55 - dentry = lookup_one_len(name, dir, len);
56 - if (!IS_ERR(dentry)) {
57 - p->is_whiteout = ovl_is_whiteout(dentry);
60 - revert_creds(old_cred);
61 - put_cred(override_cred);
62 + p->next_maybe_whiteout = rdd->first_maybe_whiteout;
63 + rdd->first_maybe_whiteout = p;
67 @@ -148,7 +128,7 @@ static int ovl_cache_entry_add_rb(struct
71 - p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
72 + p = ovl_cache_entry_new(rdd, name, len, ino, d_type);
76 @@ -169,7 +149,7 @@ static int ovl_fill_lower(struct ovl_rea
78 list_move_tail(&p->l_node, &rdd->middle);
80 - p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
81 + p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type);
85 @@ -219,6 +199,43 @@ static int ovl_fill_merge(struct dir_con
86 return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
89 +static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
93 + mutex_lock(&dir->d_inode->i_mutex);
94 + while (rdd->first_maybe_whiteout) {
95 + struct dentry *dentry;
96 + const struct cred *old_cred;
97 + struct cred *override_cred;
98 + struct ovl_cache_entry *p = rdd->first_maybe_whiteout;
100 + rdd->first_maybe_whiteout = p->next_maybe_whiteout;
102 + override_cred = prepare_creds();
103 + if (!override_cred) {
108 + * CAP_DAC_OVERRIDE for lookup
110 + cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
111 + old_cred = override_creds(override_cred);
113 + dentry = lookup_one_len(p->name, dir, p->len);
114 + if (!IS_ERR(dentry)) {
115 + p->is_whiteout = ovl_is_whiteout(dentry);
118 + revert_creds(old_cred);
119 + put_cred(override_cred);
121 + mutex_unlock(&dir->d_inode->i_mutex);
126 static inline int ovl_dir_read(struct path *realpath,
127 struct ovl_readdir_data *rdd)
129 @@ -229,7 +246,7 @@ static inline int ovl_dir_read(struct pa
130 if (IS_ERR(realfile))
131 return PTR_ERR(realfile);
133 - rdd->dir = realpath->dentry;
134 + rdd->first_maybe_whiteout = NULL;
138 @@ -238,6 +255,10 @@ static inline int ovl_dir_read(struct pa
141 } while (!err && rdd->count);
143 + if (!err && rdd->first_maybe_whiteout)
144 + err = ovl_check_whiteouts(realpath->dentry, rdd);