From 5e990591b9504c168d94de964560765633608ced Mon Sep 17 00:00:00 2001 From: Brian Quinlan Date: Mon, 1 Nov 2021 16:33:01 +0000 Subject: [PATCH] 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 Commit-Queue: Brian Quinlan --- runtime/bin/file_win.cc | 2 +- sdk/lib/io/file_system_entity.dart | 12 ++++++++++++ tests/standalone/io/file_stat_test.dart | 12 ++++++++++++ tests/standalone_2/io/file_stat_test.dart | 15 +++++++++++++-- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc index bb24d000733..6cb886095c0 100644 --- a/runtime/bin/file_win.cc +++ b/runtime/bin/file_win.cc @@ -896,7 +896,7 @@ const char* File::LinkTarget(Namespace* namespc, 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 4eedcf84a08..ca14f1f2642 100644 --- a/sdk/lib/io/file_system_entity.dart +++ b/sdk/lib/io/file_system_entity.dart @@ -93,6 +93,9 @@ class FileStat { /// 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 @@ class FileStat { /// 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 stat(String path) { final IOOverrides? overrides = IOOverrides.current; @@ -356,6 +362,9 @@ abstract class FileSystemEntity { /// Returns a `Future` 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 @@ abstract class FileSystemEntity { /// /// 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 86054da2775..8cc3e0e0544 100644 --- a/tests/standalone/io/file_stat_test.dart +++ b/tests/standalone/io/file_stat_test.dart @@ -19,11 +19,16 @@ void testStat() { 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 @@ void testStat() { 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 9af5190440b..48b05b254db 100644 --- a/tests/standalone_2/io/file_stat_test.dart +++ b/tests/standalone_2/io/file_stat_test.dart @@ -21,11 +21,16 @@ void testStat() { 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 @@ void testStat() { .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 @@ void testStat() { 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())