Move tmpdir creation logic into platform-specific files
Makes the logic testable, removes a hard to follow ifdef, and lets us get rid
of some redundant logic.
PiperOrigin-RevId: 303382036
diff --git a/src/main/cpp/archive_utils.cc b/src/main/cpp/archive_utils.cc
index aae4957..ab6445f 100644
--- a/src/main/cpp/archive_utils.cc
+++ b/src/main/cpp/archive_utils.cc
@@ -114,10 +114,10 @@
if (dumper == nullptr) {
BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR) << error;
}
- if (!blaze_util::MakeDirectories(output_dir, 0777)) {
+
+ if (!blaze_util::PathExists(output_dir)) {
BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
- << "couldn't create '" << output_dir
- << "': " << blaze_util::GetLastErrorString();
+ << "Archive output directory didn't exist: " << output_dir;
}
BAZEL_LOG(USER) << "Extracting " << product_name << " installation...";
diff --git a/src/main/cpp/archive_utils.h b/src/main/cpp/archive_utils.h
index 9410781..f970080 100644
--- a/src/main/cpp/archive_utils.h
+++ b/src/main/cpp/archive_utils.h
@@ -27,6 +27,7 @@
std::string *install_md5);
// Extracts the embedded data files in `archive_path` into `output_dir`.
+// It's expected that `output_dir` already exists and that it's a directory.
// Fails if `expected_install_md5` doesn't match that contained in the archive,
// as this could indicate that the contents has unexpectedly changed.
void ExtractArchiveOrDie(const std::string &archive_path,
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index 63007b6..4b0b5fb 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -965,29 +965,7 @@
if (!blaze_util::PathExists(install_base)) {
uint64_t st = GetMillisecondsMonotonic();
// Work in a temp dir to avoid races.
-#if defined(_WIN32)
- string tmp_install = install_base + ".tmp." + blaze::GetProcessIdAsString();
-#else
- // On Linux, we can't use the PID as a unique identifier, because Bazel
- // might run in a PID namespace and then all Bazel clients have the same
- // PID, so we use mkdtemp instead.
- if (!blaze_util::MakeDirectories(blaze_util::Dirname(install_base), 0777)) {
- BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
- << "couldn't create '" << blaze_util::Dirname(install_base)
- << "': " << blaze_util::GetLastErrorString();
- }
- std::string tmp_install(install_base + ".tmp.XXXXXX");
- if (mkdtemp(&tmp_install[0]) == nullptr) {
- std::string err = GetLastErrorString();
- BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
- << "could not create temporary directory to extract install base"
- << " (" << err << ")";
- }
- // There's no better way to get the current umask than to set and reset it.
- const mode_t um = umask(0);
- umask(um);
- chmod(tmp_install.c_str(), 0777 & ~um);
-#endif
+ string tmp_install = blaze_util::CreateTempDir(install_base + ".tmp.");
ExtractArchiveOrDie(self_path, startup_options.product_name,
expected_install_md5, tmp_install);
BlessFiles(tmp_install);
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h
index a918dce..4e8e254 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -192,6 +192,10 @@
bool MakeDirectories(const std::string &path, unsigned int mode);
bool MakeDirectories(const Path &path, unsigned int mode);
+// Creates a directory starting with prefix for temporary usage. The directory
+// name is guaranteed to be at least unique to this process.
+std::string CreateTempDir(const std::string &prefix);
+
// Removes the specified path or directory, and in the latter case, all of its
// contents. Returns true iff the path doesn't exists when the method completes
// (including if the path didn't exist to begin with). Does not follow symlinks.
diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc
index 84559b9..4a636e8 100644
--- a/src/main/cpp/util/file_posix.cc
+++ b/src/main/cpp/util/file_posix.cc
@@ -124,6 +124,33 @@
return stat_succeeded;
}
+
+string CreateTempDir(const std::string &prefix) {
+ std::string parent = Dirname(prefix);
+ // Need parent to exist first.
+ // TODO(b/150220877): Avoid trying to recreate a dir that already exists.
+ if (!blaze_util::MakeDirectories(parent, 0777)) {
+ BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
+ << "couldn't create '" << parent << "': "
+ << blaze_util::GetLastErrorString();
+ }
+
+ std::string result(prefix + "XXXXXX");
+ if (mkdtemp(&result[0]) == nullptr) {
+ std::string err = GetLastErrorString();
+ BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
+ << "could not create temporary directory to extract install base"
+ << " (" << err << ")";
+ }
+
+ // There's no better way to get the current umask than to set and reset it.
+ const mode_t um = umask(0);
+ umask(um);
+ chmod(result.c_str(), 0777 & ~um);
+
+ return result;
+}
+
static bool RemoveDirRecursively(const std::string &path) {
DIR *dir;
if ((dir = opendir(path.c_str())) == NULL) {
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index e25dab4..533ccfb 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -646,6 +646,15 @@
return MakeDirectoriesW(path.AsNativePath(), mode);
}
+string CreateTempDir(const std::string &prefix) {
+ string result = prefix + blaze_util::ToString(GetCurrentProcessId());
+ if (!blaze_util::MakeDirectories(result, 0777)) {
+ BAZEL_DIE(blaze_exit_code::INTERNAL_ERROR)
+ << "couldn't create '" << result
+ << "': " << blaze_util::GetLastErrorString();
+ }
+ return result;
+}
static bool RemoveContents(wstring path) {
static const wstring kDot(L".");
diff --git a/src/test/cpp/util/file_test.cc b/src/test/cpp/util/file_test.cc
index 869a719..e67622b 100644
--- a/src/test/cpp/util/file_test.cc
+++ b/src/test/cpp/util/file_test.cc
@@ -175,6 +175,28 @@
ASSERT_FALSE(mtime->IsUntampered(file));
}
+TEST(FileTest, TestCreateTempDir) {
+ const char* tempdir_cstr = getenv("TEST_TMPDIR");
+ EXPECT_NE(tempdir_cstr, nullptr);
+ EXPECT_NE(tempdir_cstr[0], 0);
+ string tempdir(tempdir_cstr);
+ string tmpdir(tempdir_cstr);
+
+ string prefix_in_existing_dir(JoinPath(tempdir, "foo."));
+ string result_in_existing_dir(CreateTempDir(prefix_in_existing_dir));
+ ASSERT_NE(result_in_existing_dir, prefix_in_existing_dir);
+ ASSERT_EQ(0, result_in_existing_dir.find(prefix_in_existing_dir));
+ EXPECT_TRUE(PathExists(result_in_existing_dir));
+
+ string base_dir(JoinPath(tempdir, "doesntexistyet"));
+ ASSERT_FALSE(PathExists(base_dir));
+ string prefix_in_new_dir(JoinPath(base_dir, "foo."));
+ string result_in_new_dir(CreateTempDir(prefix_in_new_dir));
+ ASSERT_NE(result_in_new_dir, prefix_in_new_dir);
+ ASSERT_EQ(0, result_in_new_dir.find(prefix_in_new_dir));
+ EXPECT_TRUE(PathExists(result_in_new_dir));
+}
+
TEST(FileTest, TestRenameDirectory) {
const char* tempdir_cstr = getenv("TEST_TMPDIR");
EXPECT_NE(tempdir_cstr, nullptr);