Skip to content

Commit

Permalink
Avoid the race condition if multiple calls to MakeDirs occur simult…
Browse files Browse the repository at this point in the history
…aneously with overlapping paths.

Based on bazelbuild#644.

PiperOrigin-RevId: 421565838
  • Loading branch information
allevato authored and swiple-rules-gardener committed Jan 13, 2022
1 parent fe3c1de commit aa8b3c6
Showing 1 changed file with 30 additions and 2 deletions.
32 changes: 30 additions & 2 deletions tools/common/file_system.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <sys/types.h>
#include <unistd.h>

#include <cerrno>
#include <iostream>
#include <string>

#ifdef __APPLE__
Expand Down Expand Up @@ -84,7 +86,13 @@ bool MakeDirs(const std::string &path, int mode) {
struct stat dir_stats;
if (stat(path.c_str(), &dir_stats) == 0) {
// Return true if the directory already exists.
return S_ISDIR(dir_stats.st_mode);
if (S_ISDIR(dir_stats.st_mode)) {
return true;
}

std::cerr << "error: path already exists but is not a directory: "
<< path << "\n";
return false;
}

// Recurse to create the parent directory.
Expand All @@ -93,5 +101,25 @@ bool MakeDirs(const std::string &path, int mode) {
}

// Create the directory that was requested.
return mkdir(path.c_str(), mode) == 0;
if (mkdir(path.c_str(), mode) == 0) {
return true;
}

// Race condition: The above call to `mkdir` could fail if there are multiple
// calls to `MakeDirs` running at the same time with overlapping paths, so
// check again to see if the directory exists despite the call failing. If it
// does, that's ok.
if (errno == EEXIST && stat(path.c_str(), &dir_stats) == 0) {
if (S_ISDIR(dir_stats.st_mode)) {
return true;
}

std::cerr << "error: path already exists but is not a directory: "
<< path << "\n";
return false;
}

std::cerr << "error: could not create directory: " << path
<< " (" << strerror(errno) << ")\n";
return false;
}

0 comments on commit aa8b3c6

Please sign in to comment.