linux/2.6.37: R.I.P.
[openwrt.git] / target / linux / generic / patches-2.6.38 / 102-overlayfs_fix_readdir_unlink_deadlock.patch
1 --- a/fs/overlayfs/overlayfs.c
2 +++ b/fs/overlayfs/overlayfs.c
3 @@ -1700,37 +1700,56 @@ static int ovl_check_empty_dir(struct de
4         return err;
5  }
6  
7 -static int ovl_unlink_whiteout(void *buf, const char *name, int namelen,
8 -                                loff_t offset, u64 ino, unsigned int d_type)
9 +static int ovl_fill_links(void *buf, const char *name, int namelen,
10 +                          loff_t offset, u64 ino, unsigned int d_type)
11  {
12         struct ovl_readdir_data *rdd = buf;
13 +       struct ovl_cache_entry *p;
14  
15 -       rdd->count++;
16 -       /* check d_type to filter out "." and ".." */
17 -       if (d_type == DT_LNK) {
18 -               struct dentry *dentry;
19 +       if (d_type != DT_LNK)
20 +               return 0;
21  
22 -               dentry = lookup_one_len(name, rdd->dir, namelen);
23 -               if (IS_ERR(dentry)) {
24 -                       rdd->err = PTR_ERR(dentry);
25 -               } else {
26 -                       rdd->err = vfs_unlink(rdd->dir->d_inode, dentry);
27 -                       dput(dentry);
28 -               }
29 -       }
30 +       p = ovl_cache_entry_new(name, namelen, ino, d_type);
31 +       if (!p)
32 +               return -ENOMEM;
33  
34 -       return rdd->err;
35 +       list_add(&p->l_node, rdd->list);
36 +       return 0;
37  }
38  
39  static int ovl_remove_whiteouts(struct dentry *dentry)
40  {
41         struct path upperpath;
42 -       struct ovl_readdir_data rdd = { .list = NULL };
43 +       LIST_HEAD(list);
44 +       struct ovl_readdir_data rdd = { .list = &list };
45 +       struct ovl_cache_entry *p, *t;
46 +       int ret;
47  
48         ovl_path_upper(dentry, &upperpath);
49         rdd.dir = upperpath.dentry;
50  
51 -       return ovl_dir_read(&upperpath, &rdd, ovl_unlink_whiteout);
52 +       ret = ovl_dir_read(&upperpath, &rdd, ovl_fill_links);
53 +
54 +       mutex_lock(&rdd.dir->d_inode->i_mutex);
55 +       list_for_each_entry_safe(p, t, &list, l_node) {
56 +               struct dentry *dentry;
57 +
58 +               if (!ret) {
59 +                       dentry = lookup_one_len(p->name, rdd.dir, p->len);
60 +                       if (IS_ERR(dentry)) {
61 +                               ret = PTR_ERR(dentry);
62 +                       } else {
63 +                               ret = vfs_unlink(rdd.dir->d_inode, dentry);
64 +                               dput(dentry);
65 +                       }
66 +               }
67 +
68 +               list_del(&p->l_node);
69 +               kfree(p);
70 +       }
71 +       mutex_unlock(&rdd.dir->d_inode->i_mutex);
72 +
73 +       return ret;
74  }
75  
76  static int ovl_rmdir(struct inode *dir, struct dentry *dentry)