Automated rollback of commit 06d2f6d8ad1a7cde19c064e54e31dad7bd8cfdcf.

*** Reason for rollback ***

We must implement path case fixing on top of Skyframe (by requesting DirectoryListingValue), rather than as a JNI method. That way clients get invalidated when a path is renamed such that only the casing changes.

*** Original change description ***

Windows: implement getCorrectCasing

Implement getCorrectCasing: a function that
normalizes a path and returns the case-correct
version of it.

See https://github.com/bazelbuild/bazel/issues/8799

Closes #9435.

PiperOrigin-RevId: 277688249
diff --git a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
index e54b923..a80f3d5 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
@@ -241,9 +241,4 @@
     return Files.readAttributes(
         file.toPath(), DosFileAttributes.class, symlinkOpts(followSymlinks));
   }
-
-  @VisibleForTesting
-  Path getCorrectCasingForTesting(Path p) throws IOException {
-    return getPath(WindowsFileOperations.getCorrectCasing(p.getPathString()));
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsFileOperations.java b/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsFileOperations.java
index 552c606..340893f 100644
--- a/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsFileOperations.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/jni/WindowsFileOperations.java
@@ -113,8 +113,6 @@
 
   private static native int nativeDeletePath(String path, String[] error);
 
-  private static native void nativeGetCorrectCasing(String path, String[] result);
-
   /** Determines whether `path` is a junction point or directory symlink. */
   public static boolean isSymlinkOrJunction(String path) throws IOException {
     WindowsJniLoader.loadJni();
@@ -261,33 +259,4 @@
         throw new IOException(String.format("Cannot delete path '%s': %s", path, error[0]));
     }
   }
-
-  /**
-   * Returns the case-corrected copy of <code>absPath</code>
-   *
-   * <p>Windows paths are case-insensitive, but the filesystem preserves the casing. For example you
-   * can create <code>C:\Foo\Bar</code> and access it as <code>c:\FOO\bar</code>, but the correct
-   * casing would be <code>C:\Foo\Bar</code>. Sometimes Bazel needs the correct casing (see
-   * https://github.com/bazelbuild/bazel/issues/8799 for example).
-   *
-   * <p>This method fixes paths to have the correct casing, up to the last existing segment of the
-   * path.
-   *
-   * @param absPath an absolute Windows path. May not be normalized (i.e. have <code>./</code> and
-   *     <code>../</code> segments) and may have <code>/</code> separators instead of <code>\\
-   *     </code>.
-   * @return an absolute, normalized Windows path. The path is case-corrected up to the last
-   *     existing segment of <code>absPath</code>. The rest of the segments are left unchanged (with
-   *     regard to casing) but are normalized and copied into the result.
-   * @throws IOException if <code>absPath</code> was empty or was not absolute
-   */
-  public static String getCorrectCasing(String absPath) throws IOException {
-    WindowsJniLoader.loadJni();
-    String[] result = new String[] {null};
-    nativeGetCorrectCasing(absPath, result);
-    if (result[0].isEmpty()) {
-      throw new IOException(String.format("Path is not absolute: '%s'", absPath));
-    }
-    return removeUncPrefixAndUseSlashes(result[0]);
-  }
 }
diff --git a/src/main/native/windows/file-jni.cc b/src/main/native/windows/file-jni.cc
index 47f5f82..63d81d8 100644
--- a/src/main/native/windows/file-jni.cc
+++ b/src/main/native/windows/file-jni.cc
@@ -141,14 +141,3 @@
   }
   return result;
 }
-
-extern "C" JNIEXPORT void JNICALL
-Java_com_google_devtools_build_lib_windows_jni_WindowsFileOperations_nativeGetCorrectCasing(
-    JNIEnv* env, jclass clazz, jstring path, jobjectArray result_holder) {
-  std::wstring wpath(bazel::windows::GetJavaWstring(env, path));
-  std::wstring result(bazel::windows::GetCorrectCasing(wpath));
-  env->SetObjectArrayElement(
-      result_holder, 0,
-      env->NewString(reinterpret_cast<const jchar*>(result.c_str()),
-                     result.size()));
-}
diff --git a/src/main/native/windows/file.cc b/src/main/native/windows/file.cc
index fa65374..cc79ead 100644
--- a/src/main/native/windows/file.cc
+++ b/src/main/native/windows/file.cc
@@ -781,61 +781,5 @@
   }
 }
 
-std::wstring GetCorrectCasing(const std::wstring& abs_path) {
-  if (!HasDriveSpecifierPrefix(abs_path.c_str())) {
-    return L"";
-  }
-  std::wstring path = Normalize(abs_path);
-  std::unique_ptr<wchar_t[]> result(new wchar_t[4 + path.size() + 1]);
-  // Ensure path starts with UNC prefix, so we can use long paths in
-  // FindFirstFileW.
-  wcscpy(result.get(), L"\\\\?\\");
-  // Copy the rest of the normalized path. (Must be normalized and use `\`
-  // separators, for the UNC prefix to work.)
-  wcscpy(result.get() + 4, path.c_str());
-  // Ensure drive letter is upper case.
-  result[4] = towupper(result[4]);
-  // Start at index 7, which is after the UNC prefix and drive segment (i.e.
-  // past `\\?\C:\`).
-  wchar_t* start = result.get() + 7;
-  // Fix the casing of each segment, from left to right.
-  while (true) {
-    // Find current segment end.
-    wchar_t* seg_end = wcschr(start, L'\\');
-    // Pretend the whole path ends at the current segment.
-    if (seg_end) {
-      *seg_end = 0;
-    }
-    // Find this path from the filesystem. The lookup is case-insensitive, but
-    // the result shows the correct casing. Because we fix the casing from left
-    // to right, only the last segment needs fixing, so we look up that
-    // particular directory (or file).
-    WIN32_FIND_DATAW metadata;
-    HANDLE handle = FindFirstFileW(result.get(), &metadata);
-    if (handle != INVALID_HANDLE_VALUE) {
-      // Found the child. The correct casing is in metadata.cFileName
-      wcscpy(start, metadata.cFileName);
-      FindClose(handle);
-      if (seg_end) {
-        // There are more path segments to fix. Restore the `\` separator.
-        *seg_end = L'\\';
-        start = seg_end + 1;
-      } else {
-        // This was the last segment.
-        break;
-      }
-    } else {
-      // Path does not exist. Restore the `\` separator and leave the rest of
-      // the path unchanged.
-      if (seg_end) {
-        *seg_end = L'\\';
-      }
-      break;
-    }
-  }
-  // Return the case-corrected path without the `\\?\` prefix.
-  return result.get() + 4;
-}
-
 }  // namespace windows
 }  // namespace bazel
diff --git a/src/main/native/windows/file.h b/src/main/native/windows/file.h
index 9786687..f8651e6 100644
--- a/src/main/native/windows/file.h
+++ b/src/main/native/windows/file.h
@@ -195,23 +195,6 @@
 
 bool GetCwd(std::wstring* result, DWORD* err_code);
 
-// Normalizes and corrects the casing of 'abs_path'.
-//
-// 'abs_path' must be absolute, and start with a Windows drive prefix.
-// It may have an UNC prefix, may not be normalized, may use '\' and '/' as
-// directory separators.
-//
-// The result is normalized, uses '\' separators, has no trailing '\', and is
-// case-corrected up to the last existing segment. Non-existent tail segments
-// are kept in their casing, but normalized.
-//
-// For example if C:\Foo\Bar exists, then
-// GetCorrectCasing("\\\\?\\c:/FOO/./bar\\") returns "C:\Foo\Bar" and
-// GetCorrectCasing("c:/foo/qux//../bar/BAZ/quX") returns "C:\Foo\Bar\BAZ\quX".
-//
-// If 'abs_path' is null or empty or not absolute, the result is empty.
-std::wstring GetCorrectCasing(const std::wstring& abs_path);
-
 }  // namespace windows
 }  // namespace bazel
 
diff --git a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
index 1efa839..dc3275c0 100644
--- a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
@@ -369,25 +369,4 @@
 
     assertThat(juncPath.readSymbolicLink()).isEqualTo(dirPath.asFragment());
   }
-
-  private static String invertCharacterCasing(String s) {
-    char[] a = s.toCharArray();
-    for (int i = 0; i < a.length; ++i) {
-      char c = a[i];
-      a[i] = Character.isUpperCase(c) ? Character.toLowerCase(c) : Character.toUpperCase(c);
-    }
-    return new String(a);
-  }
-
-  @Test
-  public void testGetCorrectCasing() throws Exception {
-    String rootStr = scratchRoot.getPathString();
-    String inverseRootStr = invertCharacterCasing(rootStr);
-    Path inverseRoot = fs.getPath(inverseRootStr);
-    assertThat(inverseRootStr).isNotEqualTo(rootStr);
-    assertThat(inverseRoot).isEqualTo(scratchRoot);
-    Path correctCasing = fs.getCorrectCasingForTesting(inverseRoot);
-    assertThat(correctCasing).isEqualTo(scratchRoot);
-    assertThat(correctCasing.getPathString()).isNotEqualTo(inverseRootStr);
-  }
 }
diff --git a/src/test/native/windows/file_test.cc b/src/test/native/windows/file_test.cc
index de3c8af..49ce133 100644
--- a/src/test/native/windows/file_test.cc
+++ b/src/test/native/windows/file_test.cc
@@ -452,64 +452,8 @@
   ASSERT_NORMALIZE("c:\\", "c:\\");
   ASSERT_NORMALIZE("c:\\..//foo/./bar/", "c:\\foo\\bar");
   ASSERT_NORMALIZE("../foo", "..\\foo");
-  ASSERT_NORMALIZE("../foo\\", "..\\foo");
-  ASSERT_NORMALIZE("../foo\\\\", "..\\foo");
-  ASSERT_NORMALIZE("..//foo\\\\", "..\\foo");
 #undef ASSERT_NORMALIZE
 }
 
-static void GetTestTempDirWithCorrectCasing(std::wstring* result,
-                                            std::wstring* result_inverted) {
-  static constexpr size_t kMaxPath = 0x8000;
-  wchar_t buf[kMaxPath];
-  ASSERT_GT(GetEnvironmentVariableW(L"TEST_TMPDIR", buf, kMaxPath), 0);
-  HANDLE h = CreateFileW(buf, 0,
-                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                         NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-  ASSERT_NE(h, INVALID_HANDLE_VALUE);
-  ASSERT_GT(GetFinalPathNameByHandleW(h, buf, kMaxPath, 0), 0);
-  CloseHandle(h);
-  *result = RemoveUncPrefixMaybe(buf);
-
-  for (wchar_t* c = buf; *c; ++c) {
-    if (*c >= L'A' && *c <= L'Z') {
-      *c = L'a' + *c - L'A';
-    } else if (*c >= L'a' && *c <= L'z') {
-      *c = L'A' + *c - L'a';
-    }
-  }
-  *result_inverted = RemoveUncPrefixMaybe(buf);
-}
-
-TEST(FileTests, TestGetCorrectCase) {
-  EXPECT_EQ(GetCorrectCasing(L""), L"");
-  EXPECT_EQ(GetCorrectCasing(L"d"), L"");
-  EXPECT_EQ(GetCorrectCasing(L"d:"), L"");
-
-  EXPECT_EQ(GetCorrectCasing(L"d:\\"), L"D:\\");
-  EXPECT_EQ(GetCorrectCasing(L"d:/"), L"D:\\");
-  EXPECT_EQ(GetCorrectCasing(L"d:\\\\"), L"D:\\");
-  EXPECT_EQ(GetCorrectCasing(L"d://"), L"D:\\");
-  EXPECT_EQ(GetCorrectCasing(L"d:\\\\\\"), L"D:\\");
-  EXPECT_EQ(GetCorrectCasing(L"d:///"), L"D:\\");
-
-  EXPECT_EQ(GetCorrectCasing(L"A:/Non:Existent"), L"A:\\Non:Existent");
-
-  EXPECT_EQ(GetCorrectCasing(L"A:\\\\Non:Existent"), L"A:\\Non:Existent");
-  EXPECT_EQ(GetCorrectCasing(L"A:\\Non:Existent\\."), L"A:\\Non:Existent");
-  EXPECT_EQ(GetCorrectCasing(L"A:\\Non:Existent\\..\\.."), L"A:\\");
-  EXPECT_EQ(GetCorrectCasing(L"A:\\Non:Existent\\.\\"), L"A:\\Non:Existent");
-
-  std::wstring tmp, tmp_inverse;
-  GetTestTempDirWithCorrectCasing(&tmp, &tmp_inverse);
-  ASSERT_GT(tmp.size(), 0);
-  ASSERT_NE(tmp, tmp_inverse);
-  EXPECT_EQ(GetCorrectCasing(tmp), tmp);
-  EXPECT_EQ(GetCorrectCasing(tmp_inverse), tmp);
-  EXPECT_EQ(GetCorrectCasing(tmp_inverse + L"\\"), tmp);
-  EXPECT_EQ(GetCorrectCasing(tmp_inverse + L"\\Does\\\\Not\\./Exist"),
-            tmp + L"\\Does\\Not\\Exist");
-}
-
 }  // namespace windows
 }  // namespace bazel