| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- Patch by: Miklos Szeredi <[email protected]>
- Some filesystems (e.g. jffs2) lock the same resources for both readdir
- and lookup, leading to a deadlock in ovl_cache_entry_new, which is called
- from the filldir, and calls lookup itself.
- --- a/fs/overlayfs/readdir.c
- +++ b/fs/overlayfs/readdir.c
- @@ -23,6 +23,7 @@ struct ovl_cache_entry {
- u64 ino;
- struct list_head l_node;
- struct rb_node node;
- + struct ovl_cache_entry *next_maybe_whiteout;
- bool is_whiteout;
- char name[];
- };
- @@ -39,7 +40,7 @@ struct ovl_readdir_data {
- struct rb_root root;
- struct list_head *list;
- struct list_head middle;
- - struct dentry *dir;
- + struct ovl_cache_entry *first_maybe_whiteout;
- int count;
- int err;
- };
- @@ -79,7 +80,7 @@ static struct ovl_cache_entry *ovl_cache
- return NULL;
- }
-
- -static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
- +static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
- const char *name, int len,
- u64 ino, unsigned int d_type)
- {
- @@ -98,29 +99,8 @@ static struct ovl_cache_entry *ovl_cache
- p->is_whiteout = false;
-
- if (d_type == DT_CHR) {
- - struct dentry *dentry;
- - const struct cred *old_cred;
- - struct cred *override_cred;
- -
- - override_cred = prepare_creds();
- - if (!override_cred) {
- - kfree(p);
- - return NULL;
- - }
- -
- - /*
- - * CAP_DAC_OVERRIDE for lookup
- - */
- - cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
- - old_cred = override_creds(override_cred);
- -
- - dentry = lookup_one_len(name, dir, len);
- - if (!IS_ERR(dentry)) {
- - p->is_whiteout = ovl_is_whiteout(dentry);
- - dput(dentry);
- - }
- - revert_creds(old_cred);
- - put_cred(override_cred);
- + p->next_maybe_whiteout = rdd->first_maybe_whiteout;
- + rdd->first_maybe_whiteout = p;
- }
- return p;
- }
- @@ -148,7 +128,7 @@ static int ovl_cache_entry_add_rb(struct
- return 0;
- }
-
- - p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
- + p = ovl_cache_entry_new(rdd, name, len, ino, d_type);
- if (p == NULL)
- return -ENOMEM;
-
- @@ -169,7 +149,7 @@ static int ovl_fill_lower(struct ovl_rea
- if (p) {
- list_move_tail(&p->l_node, &rdd->middle);
- } else {
- - p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
- + p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type);
- if (p == NULL)
- rdd->err = -ENOMEM;
- else
- @@ -219,6 +199,43 @@ static int ovl_fill_merge(struct dir_con
- return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
- }
-
- +static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
- +{
- + int err = 0;
- +
- + mutex_lock(&dir->d_inode->i_mutex);
- + while (rdd->first_maybe_whiteout) {
- + struct dentry *dentry;
- + const struct cred *old_cred;
- + struct cred *override_cred;
- + struct ovl_cache_entry *p = rdd->first_maybe_whiteout;
- +
- + rdd->first_maybe_whiteout = p->next_maybe_whiteout;
- +
- + override_cred = prepare_creds();
- + if (!override_cred) {
- + err = -ENOMEM;
- + break;
- + }
- + /*
- + * CAP_DAC_OVERRIDE for lookup
- + */
- + cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
- + old_cred = override_creds(override_cred);
- +
- + dentry = lookup_one_len(p->name, dir, p->len);
- + if (!IS_ERR(dentry)) {
- + p->is_whiteout = ovl_is_whiteout(dentry);
- + dput(dentry);
- + }
- + revert_creds(old_cred);
- + put_cred(override_cred);
- + }
- + mutex_unlock(&dir->d_inode->i_mutex);
- +
- + return err;
- +}
- +
- static inline int ovl_dir_read(struct path *realpath,
- struct ovl_readdir_data *rdd)
- {
- @@ -229,7 +246,7 @@ static inline int ovl_dir_read(struct pa
- if (IS_ERR(realfile))
- return PTR_ERR(realfile);
-
- - rdd->dir = realpath->dentry;
- + rdd->first_maybe_whiteout = NULL;
- rdd->ctx.pos = 0;
- do {
- rdd->count = 0;
- @@ -238,6 +255,10 @@ static inline int ovl_dir_read(struct pa
- if (err >= 0)
- err = rdd->err;
- } while (!err && rdd->count);
- +
- + if (!err && rdd->first_maybe_whiteout)
- + err = ovl_check_whiteouts(realpath->dentry, rdd);
- +
- fput(realfile);
-
- return err;
|