aboutsummaryrefslogtreecommitdiffstats
path: root/fs/overlayfs
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2015-06-22 07:53:48 -0400
committerMiklos Szeredi <mszeredi@suse.cz>2015-06-22 07:53:48 -0400
commitcdb672795876d7bc1870aed9a2d7cb59f43d1d96 (patch)
tree83f520fa3fc7ccf117e821e6707c9de25b9e989b /fs/overlayfs
parent7c03b5d45b8eebf0111125053d8fe887cc262ba6 (diff)
ovl: lookup whiteouts outside iterate_dir()
If jffs2 can deadlock on overlayfs readdir because it takes the same lock on ->iterate() as in ->lookup(). Fix by moving whiteout checking outside iterate_dir(). Optimized by collecting potential whiteouts (DT_CHR) in a temporary list and if non-empty iterating throug these and checking for a 0/0 chardev. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Fixes: 49c21e1cacd7 ("ovl: check whiteout while reading directory") Reported-by: Roman Yeryomin <leroi.lists@gmail.com>
Diffstat (limited to 'fs/overlayfs')
-rw-r--r--fs/overlayfs/readdir.c77
1 files changed, 49 insertions, 28 deletions
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 907870e81a72..70e9af551600 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -23,6 +23,7 @@ struct ovl_cache_entry {
23 u64 ino; 23 u64 ino;
24 struct list_head l_node; 24 struct list_head l_node;
25 struct rb_node node; 25 struct rb_node node;
26 struct ovl_cache_entry *next_maybe_whiteout;
26 bool is_whiteout; 27 bool is_whiteout;
27 char name[]; 28 char name[];
28}; 29};
@@ -39,7 +40,7 @@ struct ovl_readdir_data {
39 struct rb_root root; 40 struct rb_root root;
40 struct list_head *list; 41 struct list_head *list;
41 struct list_head middle; 42 struct list_head middle;
42 struct dentry *dir; 43 struct ovl_cache_entry *first_maybe_whiteout;
43 int count; 44 int count;
44 int err; 45 int err;
45}; 46};
@@ -79,7 +80,7 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
79 return NULL; 80 return NULL;
80} 81}
81 82
82static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir, 83static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
83 const char *name, int len, 84 const char *name, int len,
84 u64 ino, unsigned int d_type) 85 u64 ino, unsigned int d_type)
85{ 86{
@@ -98,29 +99,8 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
98 p->is_whiteout = false; 99 p->is_whiteout = false;
99 100
100 if (d_type == DT_CHR) { 101 if (d_type == DT_CHR) {
101 struct dentry *dentry; 102 p->next_maybe_whiteout = rdd->first_maybe_whiteout;
102 const struct cred *old_cred; 103 rdd->first_maybe_whiteout = p;
103 struct cred *override_cred;
104
105 override_cred = prepare_creds();
106 if (!override_cred) {
107 kfree(p);
108 return NULL;
109 }
110
111 /*
112 * CAP_DAC_OVERRIDE for lookup
113 */
114 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
115 old_cred = override_creds(override_cred);
116
117 dentry = lookup_one_len(name, dir, len);
118 if (!IS_ERR(dentry)) {
119 p->is_whiteout = ovl_is_whiteout(dentry);
120 dput(dentry);
121 }
122 revert_creds(old_cred);
123 put_cred(override_cred);
124 } 104 }
125 return p; 105 return p;
126} 106}
@@ -148,7 +128,7 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
148 return 0; 128 return 0;
149 } 129 }
150 130
151 p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type); 131 p = ovl_cache_entry_new(rdd, name, len, ino, d_type);
152 if (p == NULL) 132 if (p == NULL)
153 return -ENOMEM; 133 return -ENOMEM;
154 134
@@ -169,7 +149,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
169 if (p) { 149 if (p) {
170 list_move_tail(&p->l_node, &rdd->middle); 150 list_move_tail(&p->l_node, &rdd->middle);
171 } else { 151 } else {
172 p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type); 152 p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type);
173 if (p == NULL) 153 if (p == NULL)
174 rdd->err = -ENOMEM; 154 rdd->err = -ENOMEM;
175 else 155 else
@@ -219,6 +199,43 @@ static int ovl_fill_merge(struct dir_context *ctx, const char *name,
219 return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type); 199 return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
220} 200}
221 201
202static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
203{
204 int err;
205 struct ovl_cache_entry *p;
206 struct dentry *dentry;
207 const struct cred *old_cred;
208 struct cred *override_cred;
209
210 override_cred = prepare_creds();
211 if (!override_cred)
212 return -ENOMEM;
213
214 /*
215 * CAP_DAC_OVERRIDE for lookup
216 */
217 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
218 old_cred = override_creds(override_cred);
219
220 err = mutex_lock_killable(&dir->d_inode->i_mutex);
221 if (!err) {
222 while (rdd->first_maybe_whiteout) {
223 p = rdd->first_maybe_whiteout;
224 rdd->first_maybe_whiteout = p->next_maybe_whiteout;
225 dentry = lookup_one_len(p->name, dir, p->len);
226 if (!IS_ERR(dentry)) {
227 p->is_whiteout = ovl_is_whiteout(dentry);
228 dput(dentry);
229 }
230 }
231 mutex_unlock(&dir->d_inode->i_mutex);
232 }
233 revert_creds(old_cred);
234 put_cred(override_cred);
235
236 return err;
237}
238
222static inline int ovl_dir_read(struct path *realpath, 239static inline int ovl_dir_read(struct path *realpath,
223 struct ovl_readdir_data *rdd) 240 struct ovl_readdir_data *rdd)
224{ 241{
@@ -229,7 +246,7 @@ static inline int ovl_dir_read(struct path *realpath,
229 if (IS_ERR(realfile)) 246 if (IS_ERR(realfile))
230 return PTR_ERR(realfile); 247 return PTR_ERR(realfile);
231 248
232 rdd->dir = realpath->dentry; 249 rdd->first_maybe_whiteout = NULL;
233 rdd->ctx.pos = 0; 250 rdd->ctx.pos = 0;
234 do { 251 do {
235 rdd->count = 0; 252 rdd->count = 0;
@@ -238,6 +255,10 @@ static inline int ovl_dir_read(struct path *realpath,
238 if (err >= 0) 255 if (err >= 0)
239 err = rdd->err; 256 err = rdd->err;
240 } while (!err && rdd->count); 257 } while (!err && rdd->count);
258
259 if (!err && rdd->first_maybe_whiteout)
260 err = ovl_check_whiteouts(realpath->dentry, rdd);
261
241 fput(realfile); 262 fput(realfile);
242 263
243 return err; 264 return err;