Skip to content

Commit

Permalink
[flutter_tools] tool exit access denied during symlinking (#106213)
Browse files Browse the repository at this point in the history
  • Loading branch information
christopherfujino committed Jun 23, 2022
1 parent 9f4b9bf commit 788c8b8
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 19 deletions.
46 changes: 32 additions & 14 deletions packages/flutter_tools/lib/src/flutter_plugins.dart
Expand Up @@ -1004,20 +1004,32 @@ void createPluginSymlinks(FlutterProject project, {bool force = false, @visibleF
void handleSymlinkException(FileSystemException e, {
required Platform platform,
required OperatingSystemUtils os,
required String destination,
required String source,
}) {
if (platform.isWindows && (e.osError?.errorCode ?? 0) == 1314) {
final String? versionString = RegExp(r'[\d.]+').firstMatch(os.name)?.group(0);
final Version? version = Version.parse(versionString);
// Windows 10 14972 is the oldest version that allows creating symlinks
// just by enabling developer mode; before that it requires running the
// terminal as Administrator.
// https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/
final String instructions = (version != null && version >= Version(10, 0, 14972))
? 'Please enable Developer Mode in your system settings. Run\n'
' start ms-settings:developers\n'
'to open settings.'
: 'You must build from a terminal run as administrator.';
throwToolExit('Building with plugins requires symlink support.\n\n$instructions');
if (platform.isWindows) {
// ERROR_ACCESS_DENIED
if (e.osError?.errorCode == 5) {
throwToolExit(
'ERROR_ACCESS_DENIED file system exception thrown while trying to '
'create a symlink from $source to $destination',
);
}
// ERROR_PRIVILEGE_NOT_HELD, user cannot symlink
if (e.osError?.errorCode == 1314) {
final String? versionString = RegExp(r'[\d.]+').firstMatch(os.name)?.group(0);
final Version? version = Version.parse(versionString);
// Windows 10 14972 is the oldest version that allows creating symlinks
// just by enabling developer mode; before that it requires running the
// terminal as Administrator.
// https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/
final String instructions = (version != null && version >= Version(10, 0, 14972))
? 'Please enable Developer Mode in your system settings. Run\n'
' start ms-settings:developers\n'
'to open settings.'
: 'You must build from a terminal run as administrator.';
throwToolExit('Building with plugins requires symlink support.\n\n$instructions');
}
}
}

Expand All @@ -1043,7 +1055,13 @@ void _createPlatformPluginSymlinks(Directory symlinkDirectory, List<Object?>? pl
try {
link.createSync(path);
} on FileSystemException catch (e) {
handleSymlinkException(e, platform: globals.platform, os: globals.os);
handleSymlinkException(
e,
platform: globals.platform,
os: globals.os,
destination: 'dest',
source: 'source',
);
rethrow;
}
}
Expand Down
57 changes: 52 additions & 5 deletions packages/flutter_tools/test/general.shard/plugins_test.dart
Expand Up @@ -84,6 +84,8 @@ void main() {
// using it instead of fs must re-run any necessary setup (e.g.,
// setUpProject).
late FileSystem fsWindows;
const String pubCachePath = '/path/to/.pub-cache/hosted/pub.dartlang.org/foo-1.2.3';
const String ephemeralPackagePath = '/path/to/app/linux/flutter/ephemeral/foo-1.2.3';

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

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

expect(() => handleSymlinkException(e, platform: platform, os: os),
throwsToolExit(message: 'start ms-settings:developers'));
expect(
() => handleSymlinkException(
e,
platform: platform,
os: os,
source: pubCachePath,
destination: ephemeralPackagePath,
),
throwsToolExit(message: 'start ms-settings:developers'),
);
});

testWithoutContext('Symlink ERROR_ACCESS_DENIED failures show developers paths that were used', () async {
final Platform platform = FakePlatform(operatingSystem: 'windows');
final FakeOperatingSystemUtils os = FakeOperatingSystemUtils('Microsoft Windows [Version 10.0.14972.1]');

const FileSystemException e = FileSystemException('', '', OSError('', 5));

expect(
() => handleSymlinkException(
e,
platform: platform,
os: os,
source: pubCachePath,
destination: ephemeralPackagePath,
),
throwsToolExit(
message: 'ERROR_ACCESS_DENIED file system exception thrown while trying to create a symlink from $pubCachePath to $ephemeralPackagePath',
),
);
});

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

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

expect(() => handleSymlinkException(e, platform: platform, os: os),
throwsToolExit(message: 'administrator'));
expect(
() => handleSymlinkException(
e,
platform: platform,
os: os,
source: pubCachePath,
destination: ephemeralPackagePath,
),
throwsToolExit(message: 'administrator'),
);
});

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

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

expect(() => handleSymlinkException(e, platform: platform, os: os), returnsNormally);
expect(
() => handleSymlinkException(
e,
platform: platform,
os: os,
source: pubCachePath,
destination: ephemeralPackagePath,
),
returnsNormally,
);
});
});
}
Expand Down

0 comments on commit 788c8b8

Please sign in to comment.