summary refs log tree commit diff
path: root/fs
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2014-10-23 22:56:05 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2014-10-24 20:24:11 -0400
commit3d268c9b136f51385f9d041f3f2424501b257388 (patch)
treef2dcbda16c82c4215ccf104915e9541e1c785b85 /fs
parent1be47b387a717a1d3edf29c80b6e7f3a72ab236e (diff)
downloadlinux-3d268c9b136f51385f9d041f3f2424501b257388.tar.gz
overlayfs: don't hold ->i_mutex over opening the real directory
just use it to serialize the assignment

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/overlayfs/readdir.c19
1 files changed, 13 insertions, 6 deletions
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index c6787f84ece9..b7d9fb098840 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -458,20 +458,27 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
 	if (!od->is_upper && ovl_path_type(dentry) == OVL_PATH_MERGE) {
 		struct inode *inode = file_inode(file);
 
-		mutex_lock(&inode->i_mutex);
 		realfile = od->upperfile;
 		if (!realfile) {
 			struct path upperpath;
 
 			ovl_path_upper(dentry, &upperpath);
 			realfile = ovl_path_open(&upperpath, O_RDONLY);
-			if (IS_ERR(realfile)) {
-				mutex_unlock(&inode->i_mutex);
-				return PTR_ERR(realfile);
+			mutex_lock(&inode->i_mutex);
+			if (!od->upperfile) {
+				if (IS_ERR(realfile)) {
+					mutex_unlock(&inode->i_mutex);
+					return PTR_ERR(realfile);
+				}
+				od->upperfile = realfile;
+			} else {
+				/* somebody has beaten us to it */
+				if (!IS_ERR(realfile))
+					fput(realfile);
+				realfile = od->upperfile;
 			}
-			od->upperfile = realfile;
+			mutex_unlock(&inode->i_mutex);
 		}
-		mutex_unlock(&inode->i_mutex);
 	}
 
 	return vfs_fsync_range(realfile, start, end, datasync);