Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 788c8b8

Browse files
[flutter_tools] tool exit access denied during symlinking (#106213)
1 parent 9f4b9bf commit 788c8b8

File tree

2 files changed

+84
-19
lines changed

2 files changed

+84
-19
lines changed

packages/flutter_tools/lib/src/flutter_plugins.dart

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,20 +1004,32 @@ void createPluginSymlinks(FlutterProject project, {bool force = false, @visibleF
10041004
void handleSymlinkException(FileSystemException e, {
10051005
required Platform platform,
10061006
required OperatingSystemUtils os,
1007+
required String destination,
1008+
required String source,
10071009
}) {
1008-
if (platform.isWindows && (e.osError?.errorCode ?? 0) == 1314) {
1009-
final String? versionString = RegExp(r'[\d.]+').firstMatch(os.name)?.group(0);
1010-
final Version? version = Version.parse(versionString);
1011-
// Windows 10 14972 is the oldest version that allows creating symlinks
1012-
// just by enabling developer mode; before that it requires running the
1013-
// terminal as Administrator.
1014-
// https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/
1015-
final String instructions = (version != null && version >= Version(10, 0, 14972))
1016-
? 'Please enable Developer Mode in your system settings. Run\n'
1017-
' start ms-settings:developers\n'
1018-
'to open settings.'
1019-
: 'You must build from a terminal run as administrator.';
1020-
throwToolExit('Building with plugins requires symlink support.\n\n$instructions');
1010+
if (platform.isWindows) {
1011+
// ERROR_ACCESS_DENIED
1012+
if (e.osError?.errorCode == 5) {
1013+
throwToolExit(
1014+
'ERROR_ACCESS_DENIED file system exception thrown while trying to '
1015+
'create a symlink from $source to $destination',
1016+
);
1017+
}
1018+
// ERROR_PRIVILEGE_NOT_HELD, user cannot symlink
1019+
if (e.osError?.errorCode == 1314) {
1020+
final String? versionString = RegExp(r'[\d.]+').firstMatch(os.name)?.group(0);
1021+
final Version? version = Version.parse(versionString);
1022+
// Windows 10 14972 is the oldest version that allows creating symlinks
1023+
// just by enabling developer mode; before that it requires running the
1024+
// terminal as Administrator.
1025+
// https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/
1026+
final String instructions = (version != null && version >= Version(10, 0, 14972))
1027+
? 'Please enable Developer Mode in your system settings. Run\n'
1028+
' start ms-settings:developers\n'
1029+
'to open settings.'
1030+
: 'You must build from a terminal run as administrator.';
1031+
throwToolExit('Building with plugins requires symlink support.\n\n$instructions');
1032+
}
10211033
}
10221034
}
10231035

@@ -1043,7 +1055,13 @@ void _createPlatformPluginSymlinks(Directory symlinkDirectory, List<Object?>? pl
10431055
try {
10441056
link.createSync(path);
10451057
} on FileSystemException catch (e) {
1046-
handleSymlinkException(e, platform: globals.platform, os: globals.os);
1058+
handleSymlinkException(
1059+
e,
1060+
platform: globals.platform,
1061+
os: globals.os,
1062+
destination: 'dest',
1063+
source: 'source',
1064+
);
10471065
rethrow;
10481066
}
10491067
}

packages/flutter_tools/test/general.shard/plugins_test.dart

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ void main() {
8484
// using it instead of fs must re-run any necessary setup (e.g.,
8585
// setUpProject).
8686
late FileSystem fsWindows;
87+
const String pubCachePath = '/path/to/.pub-cache/hosted/pub.dartlang.org/foo-1.2.3';
88+
const String ephemeralPackagePath = '/path/to/app/linux/flutter/ephemeral/foo-1.2.3';
8789

8890
// Adds basic properties to the flutterProject and its subprojects.
8991
void setUpProject(FileSystem fileSystem) {
@@ -1612,8 +1614,36 @@ flutter:
16121614

16131615
const FileSystemException e = FileSystemException('', '', OSError('', 1314));
16141616

1615-
expect(() => handleSymlinkException(e, platform: platform, os: os),
1616-
throwsToolExit(message: 'start ms-settings:developers'));
1617+
expect(
1618+
() => handleSymlinkException(
1619+
e,
1620+
platform: platform,
1621+
os: os,
1622+
source: pubCachePath,
1623+
destination: ephemeralPackagePath,
1624+
),
1625+
throwsToolExit(message: 'start ms-settings:developers'),
1626+
);
1627+
});
1628+
1629+
testWithoutContext('Symlink ERROR_ACCESS_DENIED failures show developers paths that were used', () async {
1630+
final Platform platform = FakePlatform(operatingSystem: 'windows');
1631+
final FakeOperatingSystemUtils os = FakeOperatingSystemUtils('Microsoft Windows [Version 10.0.14972.1]');
1632+
1633+
const FileSystemException e = FileSystemException('', '', OSError('', 5));
1634+
1635+
expect(
1636+
() => handleSymlinkException(
1637+
e,
1638+
platform: platform,
1639+
os: os,
1640+
source: pubCachePath,
1641+
destination: ephemeralPackagePath,
1642+
),
1643+
throwsToolExit(
1644+
message: 'ERROR_ACCESS_DENIED file system exception thrown while trying to create a symlink from $pubCachePath to $ephemeralPackagePath',
1645+
),
1646+
);
16171647
});
16181648

16191649
testWithoutContext('Symlink failures instruct developers to run as administrator on older versions of Windows', () async {
@@ -1622,8 +1652,16 @@ flutter:
16221652

16231653
const FileSystemException e = FileSystemException('', '', OSError('', 1314));
16241654

1625-
expect(() => handleSymlinkException(e, platform: platform, os: os),
1626-
throwsToolExit(message: 'administrator'));
1655+
expect(
1656+
() => handleSymlinkException(
1657+
e,
1658+
platform: platform,
1659+
os: os,
1660+
source: pubCachePath,
1661+
destination: ephemeralPackagePath,
1662+
),
1663+
throwsToolExit(message: 'administrator'),
1664+
);
16271665
});
16281666

16291667
testWithoutContext('Symlink failures only give instructions for specific errors', () async {
@@ -1632,7 +1670,16 @@ flutter:
16321670

16331671
const FileSystemException e = FileSystemException('', '', OSError('', 999));
16341672

1635-
expect(() => handleSymlinkException(e, platform: platform, os: os), returnsNormally);
1673+
expect(
1674+
() => handleSymlinkException(
1675+
e,
1676+
platform: platform,
1677+
os: os,
1678+
source: pubCachePath,
1679+
destination: ephemeralPackagePath,
1680+
),
1681+
returnsNormally,
1682+
);
16361683
});
16371684
});
16381685
}

0 commit comments

Comments
 (0)