Fix a bug where windows would not currently follow symlinks in stat() calls. Also clarify documentation. TEST=Updated tests to cover stats() calls on symlinks. Bug: https://github.com/dart-lang/sdk/issues/20389 Change-Id: I8555bacc2f83cad024ad8ef7c2f23aa97069ed2e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/218671 Reviewed-by: Alexander Aprelev <aam@google.com> Commit-Queue: Brian Quinlan <bquinlan@google.com>
diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc index bb24d00..6cb8860 100644 --- a/runtime/bin/file_win.cc +++ b/runtime/bin/file_win.cc
@@ -896,7 +896,7 @@ void File::Stat(Namespace* namespc, const char* name, int64_t* data) { const char* prefixed_name = PrefixLongFilePath(name); - File::Type type = GetType(namespc, prefixed_name, false); + File::Type type = GetType(namespc, prefixed_name, true); data[kType] = type; if (type != kDoesNotExist) { struct _stat64 st;
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart index 4eedcf8..ca14f1f 100644 --- a/sdk/lib/io/file_system_entity.dart +++ b/sdk/lib/io/file_system_entity.dart
@@ -93,6 +93,9 @@ /// Calls the operating system's `stat()` function (or equivalent) on [path]. /// + /// If [path] is a symbolic link then it is resolved and results for the + /// resulting file are returned. + /// /// Returns a [FileStat] object containing the data returned by `stat()`. /// If the call fails, returns a [FileStat] object with [FileStat.type] set to /// [FileSystemEntityType.notFound] and the other fields invalid. @@ -123,6 +126,9 @@ /// Asynchronously calls the operating system's `stat()` function (or /// equivalent) on [path]. /// + /// If [path] is a symbolic link then it is resolved and results for the + /// resulting file are returned. + /// /// Returns a [Future] which completes with the same results as [statSync]. static Future<FileStat> stat(String path) { final IOOverrides? overrides = IOOverrides.current; @@ -356,6 +362,9 @@ /// Returns a `Future<FileStat>` object containing the data returned by /// `stat()`. /// + /// If [path] is a symbolic link then it is resolved and results for the + /// resulting file are returned. + /// /// If the call fails, completes the future with a [FileStat] object /// with `.type` set to [FileSystemEntityType.notFound] and the other fields /// invalid. @@ -367,6 +376,9 @@ /// /// Returns a [FileStat] object containing the data returned by `stat()`. /// + /// If [path] is a symbolic link then it is resolved and results for the + /// resulting file are returned. + /// /// If the call fails, returns a [FileStat] object with `.type` set to /// [FileSystemEntityType.notFound] and the other fields invalid. FileStat statSync() => FileStat.statSync(path);
diff --git a/tests/standalone/io/file_stat_test.dart b/tests/standalone/io/file_stat_test.dart index 86054da..8cc3e0e 100644 --- a/tests/standalone/io/file_stat_test.dart +++ b/tests/standalone/io/file_stat_test.dart
@@ -19,11 +19,16 @@ Expect.equals(FileSystemEntityType.notFound, fileStat.type); Expect.equals(FileSystemEntityType.notFound, fileStatDirect.type); file.writeAsStringSync("Dart IO library test of FileStat"); + Link link = new Link(join(directory.path, "link")); + link.createSync(file.path); new Timer(const Duration(seconds: 2), () { file.readAsStringSync(); directory.listSync(); FileStat fileStat = FileStat.statSync(file.path); FileStat fileStatDirect = file.statSync(); + FileStat linkStat = FileStat.statSync(link.path); + FileStat linkStatDirect = link.statSync(); + Expect.equals(FileSystemEntityType.file, fileStat.type); Expect.equals(32, fileStat.size); Expect.equals(FileSystemEntityType.file, fileStatDirect.type); @@ -44,6 +49,13 @@ directoryStat.changed.compareTo(directoryStat.accessed) < 0); } Expect.equals(7 << 6, directoryStat.mode & (7 << 6)); // Includes +urwx. + + // Verify that statSync resolves the link. + Expect.equals(FileSystemEntityType.file, linkStat.type); + Expect.equals(32, linkStat.size); + Expect.equals(FileSystemEntityType.file, linkStatDirect.type); + Expect.equals(32, linkStatDirect.size); + directory.deleteSync(recursive: true); }); }
diff --git a/tests/standalone_2/io/file_stat_test.dart b/tests/standalone_2/io/file_stat_test.dart index 9af5190..48b05b2 100644 --- a/tests/standalone_2/io/file_stat_test.dart +++ b/tests/standalone_2/io/file_stat_test.dart
@@ -21,11 +21,16 @@ Expect.equals(FileSystemEntityType.notFound, fileStat.type); Expect.equals(FileSystemEntityType.notFound, fileStatDirect.type); file.writeAsStringSync("Dart IO library test of FileStat"); + Link link = new Link(join(directory.path, "link")); + link.createSync(file.path); new Timer(const Duration(seconds: 2), () { file.readAsStringSync(); directory.listSync(); FileStat fileStat = FileStat.statSync(file.path); FileStat fileStatDirect = file.statSync(); + FileStat linkStat = FileStat.statSync(link.path); + FileStat linkStatDirect = link.statSync(); + Expect.equals(FileSystemEntityType.file, fileStat.type); Expect.equals(32, fileStat.size); Expect.equals(FileSystemEntityType.file, fileStatDirect.type); @@ -46,6 +51,13 @@ .isTrue(directoryStat.changed.compareTo(directoryStat.accessed) < 0); } Expect.equals(7 << 6, directoryStat.mode & (7 << 6)); // Includes +urwx. + + // Verify that statSync resolves the link. + Expect.equals(FileSystemEntityType.file, linkStat.type); + Expect.equals(32, linkStat.size); + Expect.equals(FileSystemEntityType.file, linkStatDirect.type); + Expect.equals(32, linkStatDirect.size); + directory.deleteSync(recursive: true); }); } @@ -53,8 +65,7 @@ Future testStatAsync() { return Directory.systemTemp.createTemp('dart_file_stat').then((directory) { File file = new File(join(directory.path, "file")); - return FileStat - .stat(file.path) + return FileStat.stat(file.path) .then((fileStat) => Expect.equals(FileSystemEntityType.notFound, fileStat.type)) .then((_) => file.stat())