about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRoman Perepelitsa <roman.perepelitsa@gmail.com>2020-10-23 11:42:30 +0200
committerRoman Perepelitsa <roman.perepelitsa@gmail.com>2020-10-23 11:42:30 +0200
commitead29c2a366eb2c5006d7da64963ce5f60deb684 (patch)
tree56aa0a70daa5384b386f00e937e7b42ee4c6f929
parentda534770fd5c8022e90eff5cf4c993d60e3c56f7 (diff)
downloadzsh-ead29c2a366eb2c5006d7da64963ce5f60deb684.tar.gz
zsh-ead29c2a366eb2c5006d7da64963ce5f60deb684.tar.xz
zsh-ead29c2a366eb2c5006d7da64963ce5f60deb684.zip
Fix a race condition in zf_mkdir -p
If ~/foo does not exist and `zf_mkdir -p zf_mkdir -p` is executed
concurrently in multiple shells, it was possible prior to this patch
for the command to fail with EEXIST.
-rw-r--r--ChangeLog5
-rw-r--r--Src/Modules/files.c28
2 files changed, 24 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index c6569edb7..5650bdd68 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2020-10-18  Roman Perepelitsa  <roman.perepelitsa@gmail.com>
+
+	* 47476: Src/Modules/files.c: Fix a race condition in zf_mkdir -p
+	(based on the patch by Matthew Martin in workers/47436)
+
 2020-10-18  Axel Beckert  <abe@deuxchevaux.org>
 
 	* 47468: Doc/Zsh/contrib.yo: Fix typo
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index 6d20e38a8..7ebacba6c 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -122,19 +122,29 @@ domkdir(char *nam, char *path, mode_t mode, int p)
 {
     int err;
     mode_t oumask;
+    struct stat st;
+    int n = 8;
     char const *rpath = unmeta(path);
 
-    if(p) {
-	struct stat st;
-
-	if(!stat(rpath, &st) && S_ISDIR(st.st_mode))
+    while(n-- > 0) {
+	oumask = umask(0);
+	err = mkdir(rpath, mode) ? errno : 0;
+	umask(oumask);
+	if (!err)
+	    return 0;
+	if(!p || err != EEXIST)
+	    break;
+	if(stat(rpath, &st)) {
+	    if(errno == ENOENT)
+		continue;
+	    err = errno;
+	    break;
+	}
+	if(S_ISDIR(st.st_mode))
 	    return 0;
+	break;
     }
-    oumask = umask(0);
-    err = mkdir(rpath, mode) ? errno : 0;
-    umask(oumask);
-    if(!err)
-	return 0;
+
     zwarnnam(nam, "cannot make directory `%s': %e", path, err);
     return 1;
 }