Windows: Remove read-only attribute for directory before deleting

If some directory is set to read-only on Windows, DeletePath will fail
to delete that directory even when no one else is using it.

Previously, we only remove the read-only attribute for files before
deleting, we should also do the same for deleting directory.

Fixes https://github.com/bazelbuild/continuous-integration/issues/1012

Closes #11982.

PiperOrigin-RevId: 327774854
diff --git a/src/main/native/windows/file.cc b/src/main/native/windows/file.cc
index 78f63e0..3d55833 100644
--- a/src/main/native/windows/file.cc
+++ b/src/main/native/windows/file.cc
@@ -629,6 +629,15 @@
                                   GetLastError(), error);
   }
 
+  if (attr & FILE_ATTRIBUTE_READONLY) {
+    // Remove the read-only attribute.
+    attr &= ~FILE_ATTRIBUTE_READONLY;
+    if (!SetFileAttributesW(wpath, attr)) {
+      return GetResultFromErrorCode(L"SetFileAttributesW", path, GetLastError(),
+                                    error);
+    }
+  }
+
   if (attr & FILE_ATTRIBUTE_DIRECTORY) {
     // It's a directory or a junction, RemoveDirectoryW should be used.
     //
@@ -700,14 +709,6 @@
     }
   } else {
     // It's a regular file or symlink, DeleteFileW should be used.
-    if (attr & FILE_ATTRIBUTE_READONLY) {
-      // Remove the read-only attribute.
-      attr &= ~FILE_ATTRIBUTE_READONLY;
-      if (!SetFileAttributesW(wpath, attr)) {
-        return GetResultFromErrorCode(L"SetFileAttributesW", path,
-                                      GetLastError(), error);
-      }
-    }
     if (!DeleteFileW(wpath)) {
       // Failed to delete the file or symlink.
       return GetResultFromErrorCode(L"DeleteFileW", path,
diff --git a/src/test/py/bazel/bazel_windows_test.py b/src/test/py/bazel/bazel_windows_test.py
index e87c6d5..7390fc3 100644
--- a/src/test/py/bazel/bazel_windows_test.py
+++ b/src/test/py/bazel/bazel_windows_test.py
@@ -267,6 +267,28 @@
     )
     self.AssertExitCode(exit_code, 0, stderr)
 
+  def testDeleteReadOnlyFileAndDirectory(self):
+    self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
+    self.ScratchFile('BUILD', [
+        'genrule(',
+        '  name = "gen_read_only_dir",',
+        '  cmd_bat = "mkdir $@ && attrib +r $@",',
+        '  outs = ["dir_foo"],',
+        ')',
+        '',
+        'genrule(',
+        '  name = "gen_read_only_file",',
+        '  cmd_bat = "echo hello > $@ && attrib +r $@",',
+        '  outs = ["file_foo"],',
+        ')',
+    ])
+
+    exit_code, _, stderr = self.RunBazel(['build', '//...'])
+    self.AssertExitCode(exit_code, 0, stderr)
+
+    exit_code, _, stderr = self.RunBazel(['clean'])
+    self.AssertExitCode(exit_code, 0, stderr)
+
 
 if __name__ == '__main__':
   unittest.main()