[PATCH 2/2] overlayfs: fix a deadlock in ovl_remove_whiteouts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Calling lookup or vfs_unlink from the filldir callback leads to a deadlock,
so when removing whiteouts for a directory, read them into a list first,
then remove all entries afterwards.

Signed-off-by: Felix Fietkau <nbd@xxxxxxxxxxx>
---
 fs/overlayfs/overlayfs.c |   53 +++++++++++++++++++++++++++++++--------------
 1 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/fs/overlayfs/overlayfs.c b/fs/overlayfs/overlayfs.c
index 10ca9c0..f795699 100644
--- a/fs/overlayfs/overlayfs.c
+++ b/fs/overlayfs/overlayfs.c
@@ -1700,37 +1700,56 @@ static int ovl_check_empty_dir(struct dentry *dentry)
 	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)
-- 
1.7.3.2

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux