[vm/file] Ensure that appropriate type is reported for links to files vs directories

Bug: https://github.com/dart-lang/sdk/issues/30410
Change-Id: I96c9ca8a5aff4a4337022047ea0b844335efdfcf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/161260
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Zichang Guo <zichangguo@google.com>
diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc
index 848036a..21f8df2 100644
--- a/runtime/bin/file_win.cc
+++ b/runtime/bin/file_win.cc
@@ -886,15 +886,22 @@
     result = kDoesNotExist;
   } else if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
     if (follow_links) {
-      HANDLE dir_handle =
+      HANDLE target_handle =
           CreateFileW(name.wide(), 0,
                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                       NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-      if (dir_handle == INVALID_HANDLE_VALUE) {
+      if (target_handle == INVALID_HANDLE_VALUE) {
         result = File::kIsLink;
       } else {
-        CloseHandle(dir_handle);
-        result = File::kIsDirectory;
+        BY_HANDLE_FILE_INFORMATION info;
+        if (!GetFileInformationByHandle(target_handle, &info)) {
+          CloseHandle(target_handle);
+          return File::kIsLink;
+        }
+        CloseHandle(target_handle);
+        return ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+                   ? File::kIsDirectory
+                   : File::kIsFile;
       }
     } else {
       result = kIsLink;
diff --git a/tests/standalone/io/windows_file_system_links_test.dart b/tests/standalone/io/windows_file_system_links_test.dart
index ccf38e5..dcbc571 100644
--- a/tests/standalone/io/windows_file_system_links_test.dart
+++ b/tests/standalone/io/windows_file_system_links_test.dart
@@ -2,9 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:expect/expect.dart';
 import "dart:io";
-import "dart:isolate";
+
+import 'package:expect/expect.dart';
 
 testJunctionTypeDelete() {
   var temp =
@@ -75,9 +75,57 @@
   });
 }
 
+void testLinkToFile() {
+  final temp =
+      Directory.systemTemp.createTempSync('dart_windows_file_system_links');
+  // Create file
+  File file = new File(temp.path + Platform.pathSeparator + "test-file.tmp");
+  file.createSync();
+  // Create link pointing to the file above
+  Link link = new Link(temp.path + Platform.pathSeparator + "test-link.lnk");
+  link.createSync(file.path);
+
+  Link link2 = new Link(temp.path + Platform.pathSeparator + "test-link2.lnk");
+  link2.createSync(link.path);
+
+  try {
+    Expect.isTrue(FileSystemEntity.isLinkSync(link.path));
+    Expect.isTrue(FileSystemEntity.isLinkSync(link2.path));
+    Expect.isTrue(FileSystemEntity.isFileSync(link2.path));
+    Expect.isFalse(FileSystemEntity.isDirectorySync(link2.path));
+  } finally {
+    temp.deleteSync(recursive: true);
+  }
+}
+
+void testLinkToDirectory() {
+  final temp =
+      Directory.systemTemp.createTempSync('dart_windows_file_system_links');
+  // Create file
+  Directory dir = Directory(temp.path + Platform.pathSeparator + "test-dir");
+  dir.createSync();
+  // Create link pointing to the file above
+  Link link = Link(temp.path + Platform.pathSeparator + "test-link.lnk");
+  link.createSync(dir.path);
+
+  Link link2 = Link(temp.path + Platform.pathSeparator + "test-link2.lnk");
+  link2.createSync(link.path);
+
+  try {
+    Expect.isTrue(FileSystemEntity.isLinkSync(link.path));
+    Expect.isTrue(FileSystemEntity.isLinkSync(link2.path));
+    Expect.isFalse(FileSystemEntity.isFileSync(link2.path));
+    Expect.isTrue(FileSystemEntity.isDirectorySync(link2.path));
+  } finally {
+    temp.deleteSync(recursive: true);
+  }
+}
+
 main() {
   // Links on other platforms are tested by file_system_[async_]links_test.
   if (Platform.operatingSystem == 'windows') {
     testJunctionTypeDelete();
+    testLinkToFile();
+    testLinkToDirectory();
   }
 }
diff --git a/tests/standalone_2/io/windows_file_system_links_test.dart b/tests/standalone_2/io/windows_file_system_links_test.dart
index ccf38e5..dcbc571 100644
--- a/tests/standalone_2/io/windows_file_system_links_test.dart
+++ b/tests/standalone_2/io/windows_file_system_links_test.dart
@@ -2,9 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'package:expect/expect.dart';
 import "dart:io";
-import "dart:isolate";
+
+import 'package:expect/expect.dart';
 
 testJunctionTypeDelete() {
   var temp =
@@ -75,9 +75,57 @@
   });
 }
 
+void testLinkToFile() {
+  final temp =
+      Directory.systemTemp.createTempSync('dart_windows_file_system_links');
+  // Create file
+  File file = new File(temp.path + Platform.pathSeparator + "test-file.tmp");
+  file.createSync();
+  // Create link pointing to the file above
+  Link link = new Link(temp.path + Platform.pathSeparator + "test-link.lnk");
+  link.createSync(file.path);
+
+  Link link2 = new Link(temp.path + Platform.pathSeparator + "test-link2.lnk");
+  link2.createSync(link.path);
+
+  try {
+    Expect.isTrue(FileSystemEntity.isLinkSync(link.path));
+    Expect.isTrue(FileSystemEntity.isLinkSync(link2.path));
+    Expect.isTrue(FileSystemEntity.isFileSync(link2.path));
+    Expect.isFalse(FileSystemEntity.isDirectorySync(link2.path));
+  } finally {
+    temp.deleteSync(recursive: true);
+  }
+}
+
+void testLinkToDirectory() {
+  final temp =
+      Directory.systemTemp.createTempSync('dart_windows_file_system_links');
+  // Create file
+  Directory dir = Directory(temp.path + Platform.pathSeparator + "test-dir");
+  dir.createSync();
+  // Create link pointing to the file above
+  Link link = Link(temp.path + Platform.pathSeparator + "test-link.lnk");
+  link.createSync(dir.path);
+
+  Link link2 = Link(temp.path + Platform.pathSeparator + "test-link2.lnk");
+  link2.createSync(link.path);
+
+  try {
+    Expect.isTrue(FileSystemEntity.isLinkSync(link.path));
+    Expect.isTrue(FileSystemEntity.isLinkSync(link2.path));
+    Expect.isFalse(FileSystemEntity.isFileSync(link2.path));
+    Expect.isTrue(FileSystemEntity.isDirectorySync(link2.path));
+  } finally {
+    temp.deleteSync(recursive: true);
+  }
+}
+
 main() {
   // Links on other platforms are tested by file_system_[async_]links_test.
   if (Platform.operatingSystem == 'windows') {
     testJunctionTypeDelete();
+    testLinkToFile();
+    testLinkToDirectory();
   }
 }