diff options
author | Roman Perepelitsa <roman.perepelitsa@gmail.com> | 2020-10-23 11:42:30 +0200 |
---|---|---|
committer | Roman Perepelitsa <roman.perepelitsa@gmail.com> | 2020-10-23 11:42:30 +0200 |
commit | ead29c2a366eb2c5006d7da64963ce5f60deb684 (patch) | |
tree | 56aa0a70daa5384b386f00e937e7b42ee4c6f929 /Src | |
parent | da534770fd5c8022e90eff5cf4c993d60e3c56f7 (diff) | |
download | zsh-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.
Diffstat (limited to 'Src')
-rw-r--r-- | Src/Modules/files.c | 28 |
1 files changed, 19 insertions, 9 deletions
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; } |