Windows: remove duplicate symlink resolution code

In this PR:

- Remove the SymlinkResolver class and the
  ReadSymlinkW method from file_windows.cc, and
  the declaration from file_windows.h, because
  nothing in the Bazel client used these.

- Use the JNI library's ReadSymlinkOrJunction in
  build-runfiles-windows.cc

Closes #7942.

PiperOrigin-RevId: 241921634
diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h
index 005257a..7457e5e 100644
--- a/src/main/cpp/util/file_platform.h
+++ b/src/main/cpp/util/file_platform.h
@@ -209,13 +209,6 @@
 std::wstring GetCwdW();
 bool MakeDirectoriesW(const std::wstring &path, unsigned int mode);
 
-// Resolve a symlink to its target.
-// If `path` is a symlink, result will contain the target it points to,
-// If `path` is a not a symlink, result will contain `path` itself.
-// If the resolving succeeds, this function returns true,
-// otherwise it returns false.
-bool ReadSymlinkW(const std::wstring &path, std::wstring *result);
-
 // Check if `path` is a directory.
 bool IsDirectoryW(const std::wstring &path);
 
diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc
index 62a19f6..cdcfa54 100644
--- a/src/main/cpp/util/file_windows.cc
+++ b/src/main/cpp/util/file_windows.cc
@@ -461,97 +461,6 @@
   }
 }
 
-class SymlinkResolver {
- public:
-  SymlinkResolver();
-
-  // Resolves symlink to its actual path.
-  //
-  // Returns true if `path` is not a symlink and it exists.
-  // Returns true if `path` is a symlink and can be successfully resolved.
-  // Returns false otherwise.
-  //
-  // If `result` is not nullptr and the method returned true, then this will be
-  // reset to point to a new WCHAR buffer containing the resolved path.
-  // If `path` is a symlink, this will be the resolved path, otherwise
-  // it will be a copy of `path`.
-  bool Resolve(const WCHAR* path, std::unique_ptr<WCHAR[]>* result);
-
- private:
-  // Symbolic Link Reparse Data Buffer is described at:
-  // https://msdn.microsoft.com/en-us/library/cc232006.aspx
-  typedef struct _ReparseSymbolicLinkData {
-    static const int kSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
-    ULONG ReparseTag;
-    USHORT ReparseDataLength;
-    USHORT Reserved;
-    USHORT SubstituteNameOffset;
-    USHORT SubstituteNameLength;
-    USHORT PrintNameOffset;
-    USHORT PrintNameLength;
-    ULONG Flags;
-    WCHAR PathBuffer[1];
-  } ReparseSymbolicLinkData;
-
-  uint8_t reparse_buffer_bytes_[ReparseSymbolicLinkData::kSize];
-  ReparseSymbolicLinkData* reparse_buffer_;
-};
-
-SymlinkResolver::SymlinkResolver()
-    : reparse_buffer_(
-          reinterpret_cast<ReparseSymbolicLinkData*>(reparse_buffer_bytes_)) {}
-
-bool SymlinkResolver::Resolve(const WCHAR* path, unique_ptr<WCHAR[]>* result) {
-  DWORD attributes = ::GetFileAttributesW(path);
-  if (attributes == INVALID_FILE_ATTRIBUTES) {
-    // `path` does not exist.
-    return false;
-  } else {
-    if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
-      bool is_dir = attributes & FILE_ATTRIBUTE_DIRECTORY;
-      AutoHandle handle(CreateFileW(path, FILE_READ_EA, kAllShare, NULL,
-                                    OPEN_EXISTING,
-                                    (is_dir ? FILE_FLAG_BACKUP_SEMANTICS : 0) |
-                                        FILE_FLAG_OPEN_REPARSE_POINT,
-                                    NULL));
-      if (!handle.IsValid()) {
-        // Opening the symlink failed for whatever reason. For all intents and
-        // purposes we can treat this file as if it didn't exist.
-        return false;
-      }
-      // Read out the reparse point data.
-      DWORD bytes_returned;
-      BOOL ok = ::DeviceIoControl(
-          handle, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse_buffer_,
-          MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes_returned, NULL);
-      if (!ok) {
-        // Reading the symlink data failed. For all intents and purposes we can
-        // treat this file as if it didn't exist.
-        return false;
-      }
-      if (reparse_buffer_->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
-        if (result) {
-          size_t len = reparse_buffer_->SubstituteNameLength / sizeof(WCHAR);
-          result->reset(new WCHAR[len + 1]);
-          const WCHAR* substituteName =
-              reparse_buffer_->PathBuffer +
-              (reparse_buffer_->SubstituteNameOffset / sizeof(WCHAR));
-          wcsncpy_s(result->get(), len + 1, substituteName, len);
-          result->get()[len] = UNICODE_NULL;
-        }
-        return true;
-      }
-    }
-  }
-  // `path` is a normal file or directory.
-  if (result) {
-    size_t len = wcslen(path) + 1;
-    result->reset(new WCHAR[len]);
-    memcpy(result->get(), path, len * sizeof(WCHAR));
-  }
-  return true;
-}
-
 bool ReadDirectorySymlink(const string& name, string* result) {
   wstring wname;
   string error;
@@ -569,23 +478,6 @@
   return true;
 }
 
-bool ReadSymlinkW(const wstring& name, wstring* result) {
-  wstring wname;
-  string error;
-  if (!AsAbsoluteWindowsPath(name, &wname, &error)) {
-    BAZEL_DIE(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR)
-        << "ReadSymlinkW(" << name
-        << "): AsAbsoluteWindowsPath failed: " << error;
-    return false;
-  }
-  unique_ptr<WCHAR[]> result_ptr;
-  if (!SymlinkResolver().Resolve(wname.c_str(), &result_ptr)) {
-    return false;
-  }
-  *result = RemoveUncPrefixMaybe(result_ptr.get());
-  return true;
-}
-
 bool PathExists(const string& path) {
   if (path.empty()) {
     return false;
diff --git a/src/main/native/windows/BUILD b/src/main/native/windows/BUILD
index b4b0f18..a0b5f4b 100644
--- a/src/main/native/windows/BUILD
+++ b/src/main/native/windows/BUILD
@@ -25,6 +25,7 @@
     hdrs = ["file.h"],
     visibility = [
         "//src/main/cpp:__subpackages__",
+        "//src/main/tools:__pkg__",
         "//src/test/cpp:__subpackages__",
         "//src/test/native:__subpackages__",
         "//tools/test:__pkg__",
diff --git a/src/main/tools/BUILD b/src/main/tools/BUILD
index d1b45f8..d7e518e 100644
--- a/src/main/tools/BUILD
+++ b/src/main/tools/BUILD
@@ -53,7 +53,10 @@
         "//src/conditions:windows": ["build-runfiles-windows.cc"],
         "//conditions:default": ["build-runfiles.cc"],
     }),
-    deps = ["//src/main/cpp/util:filesystem"],
+    deps = ["//src/main/cpp/util:filesystem"] + select({
+        "//src/conditions:windows": ["//src/main/native/windows:lib-file"],
+        "//conditions:default": [],
+    }),
 )
 
 cc_binary(
diff --git a/src/main/tools/build-runfiles-windows.cc b/src/main/tools/build-runfiles-windows.cc
index edb1c91..e367228 100644
--- a/src/main/tools/build-runfiles-windows.cc
+++ b/src/main/tools/build-runfiles-windows.cc
@@ -23,6 +23,7 @@
 #include "src/main/cpp/util/file_platform.h"
 #include "src/main/cpp/util/path_platform.h"
 #include "src/main/cpp/util/strings.h"
+#include "src/main/native/windows/file.h"
 
 using std::ifstream;
 using std::string;
@@ -104,6 +105,30 @@
   str.erase(str.find_last_not_of(' ') + 1);
 }
 
+bool ReadSymlink(const wstring& abs_path, wstring* target, wstring* error) {
+  switch (bazel::windows::ReadSymlinkOrJunction(abs_path, target, error)) {
+    case bazel::windows::ReadSymlinkOrJunctionResult::kSuccess:
+      return true;
+    case bazel::windows::ReadSymlinkOrJunctionResult::kAccessDenied:
+      *error = L"access is denied";
+      break;
+    case bazel::windows::ReadSymlinkOrJunctionResult::kDoesNotExist:
+      *error = L"path does not exist";
+      break;
+    case bazel::windows::ReadSymlinkOrJunctionResult::kNotALink:
+      *error = L"path is not a link";
+      break;
+    case bazel::windows::ReadSymlinkOrJunctionResult::kUnknownLinkType:
+      *error = L"unknown link type";
+      break;
+    default:
+      // This is bazel::windows::ReadSymlinkOrJunctionResult::kError (1).
+      // The JNI code puts a custom message in 'error'.
+      break;
+  }
+  return false;
+}
+
 }  // namespace
 
 class RunfilesCreator {
@@ -266,10 +291,10 @@
         bool is_symlink =
             (metadata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
         if (is_symlink) {
-          wstring target;
-          if (!blaze_util::ReadSymlinkW(subpath, &target)) {
+          wstring target, werror;
+          if (!ReadSymlink(subpath, &target, &werror)) {
             die(L"ReadSymlinkW failed (%s): %hs", subpath.c_str(),
-                GetLastErrorString().c_str());
+                werror.c_str());
           }
 
           target = AsAbsoluteWindowsPath(target.c_str());