aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/cpp/util/file_posix.cc8
-rw-r--r--src/main/cpp/util/file_windows.cc36
-rw-r--r--src/test/cpp/util/file_windows_test.cc31
3 files changed, 65 insertions, 10 deletions
diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc
index c1a313a1f3..cc8cadbcf0 100644
--- a/src/main/cpp/util/file_posix.cc
+++ b/src/main/cpp/util/file_posix.cc
@@ -209,15 +209,15 @@ bool WriteFile(const void *data, size_t size, const string &filename) {
return result;
}
-bool UnlinkPath(const string &file_path) {
- return unlink(file_path.c_str()) == 0;
-}
-
// TODO(bazel-team): implement all functions in file_windows.cc, use them from
// MSYS, remove file_posix.cc from the `srcs` of
// //src/main/cpp/util:file when building for MSYS, and remove all
// #ifndef __CYGWIN__ directives.
#ifndef __CYGWIN__
+bool UnlinkPath(const string &file_path) {
+ return unlink(file_path.c_str()) == 0;
+}
+
bool PathExists(const string& path) {
return access(path.c_str(), F_OK) == 0;
}
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 4293d5ead7..22ab715f23 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -43,6 +43,12 @@ static unique_ptr<WCHAR[]> GetCwdW();
// necessary.
static bool IsDirectoryW(const wstring& path);
+// Returns true the file or junction at `path` is successfully deleted.
+// Returns false otherwise, or if `path` doesn't exist or is a directory.
+// `path` must be a normalized Windows path, with UNC prefix (and absolute) if
+// necessary.
+static bool UnlinkPathW(const wstring& path);
+
// Like `AsWindowsPath` but the result is absolute and has UNC prefix if needed.
static bool AsWindowsPathWithUncPrefix(const string& path, wstring* wpath);
@@ -389,14 +395,32 @@ bool WriteFile(const void* data, size_t size, const string& filename) {
#else // not COMPILER_MSVC
#endif // COMPILER_MSVC
-#ifdef COMPILER_MSVC
+static bool UnlinkPathW(const wstring& path) {
+ DWORD attrs = ::GetFileAttributesW(path.c_str());
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ // Path does not exist.
+ return false;
+ }
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
+ if (!(attrs & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ // Path is a directory; unlink(2) also cannot remove directories.
+ return false;
+ }
+ // Otherwise it's a junction, remove using RemoveDirectoryW.
+ return ::RemoveDirectoryW(path.c_str()) == TRUE;
+ } else {
+ // Otherwise it's a file, remove using DeleteFileW.
+ return ::DeleteFileW(path.c_str()) == TRUE;
+ }
+}
+
bool UnlinkPath(const string& file_path) {
- // TODO(bazel-team): implement this.
- pdie(255, "blaze_util::UnlinkPath is not implemented on Windows");
- return false;
+ wstring wpath;
+ if (!AsWindowsPathWithUncPrefix(file_path, &wpath)) {
+ return false;
+ }
+ return UnlinkPathW(wpath);
}
-#else // not COMPILER_MSVC
-#endif // COMPILER_MSVC
HANDLE OpenDirectory(const WCHAR* path, bool read_write) {
return ::CreateFileW(
diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc
index 5c2f68ee27..95460559cb 100644
--- a/src/test/cpp/util/file_windows_test.cc
+++ b/src/test/cpp/util/file_windows_test.cc
@@ -326,4 +326,35 @@ TEST(FileTest, TestIsDirectory) {
ASSERT_EQ(0, rmdir(junc1.c_str()));
}
+TEST(FileTest, TestUnlinkPath) {
+ string tmpdir(GetTestTmpDir());
+ ASSERT_LT(0, tmpdir.size());
+ ASSERT_TRUE(PathExists(tmpdir));
+
+ // Create a directory under `tempdir`, a file inside it, and a junction
+ // pointing to it.
+ string dir1(JoinPath(tmpdir, "dir1"));
+ ASSERT_EQ(0, mkdir(dir1.c_str()));
+ FILE* fh = fopen(JoinPath(dir1, "foo.txt").c_str(), "wt");
+ ASSERT_NE(nullptr, fh);
+ ASSERT_LT(0, fprintf(fh, "hello\n"));
+ fclose(fh);
+ string junc1(JoinPath(tmpdir, "junc1"));
+ RunCommand(string("cmd.exe /C mklink /J \"") + junc1 + "\" \"" + dir1 +
+ "\" >NUL 2>NUL");
+ ASSERT_TRUE(PathExists(junc1));
+ ASSERT_TRUE(PathExists(JoinPath(junc1, "foo.txt")));
+
+ // Non-existent files cannot be unlinked.
+ ASSERT_FALSE(UnlinkPath("does.not.exist"));
+ // Directories cannot be unlinked.
+ ASSERT_FALSE(UnlinkPath(dir1));
+ // Junctions can be unlinked, even if the pointed directory is not empty.
+ ASSERT_TRUE(UnlinkPath(JoinPath(junc1, "foo.txt")));
+ // Files can be unlinked.
+ ASSERT_TRUE(UnlinkPath(junc1));
+ // Clean up the now empty directory.
+ ASSERT_EQ(0, rmdir(dir1.c_str()));
+}
+
} // namespace blaze_util