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