Unverified Commit c1c12aa3 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Add posix permission chown suggestion to io error handling (#81942)

parent 0581c05c
...@@ -212,6 +212,7 @@ class ErrorHandlingFile ...@@ -212,6 +212,7 @@ class ErrorHandlingFile
)), )),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to write to a file at "${delegate.path}"', failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
); );
} }
...@@ -221,6 +222,7 @@ class ErrorHandlingFile ...@@ -221,6 +222,7 @@ class ErrorHandlingFile
() => delegate.readAsStringSync(), () => delegate.readAsStringSync(),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to read a file at "${delegate.path}"', failureMessage: 'Flutter failed to read a file at "${delegate.path}"',
posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
); );
} }
...@@ -234,6 +236,7 @@ class ErrorHandlingFile ...@@ -234,6 +236,7 @@ class ErrorHandlingFile
() => delegate.writeAsBytesSync(bytes, mode: mode, flush: flush), () => delegate.writeAsBytesSync(bytes, mode: mode, flush: flush),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to write to a file at "${delegate.path}"', failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
); );
} }
...@@ -253,6 +256,7 @@ class ErrorHandlingFile ...@@ -253,6 +256,7 @@ class ErrorHandlingFile
)), )),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to write to a file at "${delegate.path}"', failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
); );
} }
...@@ -272,6 +276,7 @@ class ErrorHandlingFile ...@@ -272,6 +276,7 @@ class ErrorHandlingFile
), ),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to write to a file at "${delegate.path}"', failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
); );
} }
...@@ -283,6 +288,7 @@ class ErrorHandlingFile ...@@ -283,6 +288,7 @@ class ErrorHandlingFile
), ),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to create file at "${delegate.path}"', failureMessage: 'Flutter failed to create file at "${delegate.path}"',
posixPermissionSuggestion: recursive ? null : _posixPermissionSuggestion(<String>[delegate.parent.path]),
); );
} }
...@@ -294,6 +300,7 @@ class ErrorHandlingFile ...@@ -294,6 +300,7 @@ class ErrorHandlingFile
), ),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to open a file at "${delegate.path}"', failureMessage: 'Flutter failed to open a file at "${delegate.path}"',
posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
); );
} }
...@@ -307,7 +314,8 @@ class ErrorHandlingFile ...@@ -307,7 +314,8 @@ class ErrorHandlingFile
_runSync<void>( _runSync<void>(
() => delegate.openSync(mode: FileMode.read).closeSync(), () => delegate.openSync(mode: FileMode.read).closeSync(),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to copy $path to $newPath due to source location error' failureMessage: 'Flutter failed to copy $path to $newPath due to source location error',
posixPermissionSuggestion: _posixPermissionSuggestion(<String>[path]),
); );
// Next check if the destination file can be written. If not, bail through // Next check if the destination file can be written. If not, bail through
// error handling. // error handling.
...@@ -347,11 +355,17 @@ class ErrorHandlingFile ...@@ -347,11 +355,17 @@ class ErrorHandlingFile
source?.closeSync(); source?.closeSync();
sink?.closeSync(); sink?.closeSync();
} }
}, platform: _platform, failureMessage: 'Flutter failed to copy $path to $newPath due to unknown error'); }, platform: _platform,
failureMessage: 'Flutter failed to copy $path to $newPath due to unknown error',
posixPermissionSuggestion: _posixPermissionSuggestion(<String>[path, resultFile.parent.path]),
);
// The original copy failed, but the manual copy worked. // The original copy failed, but the manual copy worked.
return wrapFile(resultFile); return wrapFile(resultFile);
} }
String _posixPermissionSuggestion(List<String> paths) => 'Try running:\n'
' sudo chown -R \$(whoami) ${paths.map(fileSystem.path.absolute).join(' ')}';
@override @override
String toString() => delegate.toString(); String toString() => delegate.toString();
} }
...@@ -420,6 +434,7 @@ class ErrorHandlingDirectory ...@@ -420,6 +434,7 @@ class ErrorHandlingDirectory
platform: _platform, platform: _platform,
failureMessage: failureMessage:
'Flutter failed to create a directory at "${delegate.path}"', 'Flutter failed to create a directory at "${delegate.path}"',
posixPermissionSuggestion: recursive ? null : _posixPermissionSuggestion(delegate.parent.path),
); );
} }
...@@ -450,6 +465,7 @@ class ErrorHandlingDirectory ...@@ -450,6 +465,7 @@ class ErrorHandlingDirectory
platform: _platform, platform: _platform,
failureMessage: failureMessage:
'Flutter failed to create a directory at "${delegate.path}"', 'Flutter failed to create a directory at "${delegate.path}"',
posixPermissionSuggestion: recursive ? null : _posixPermissionSuggestion(delegate.parent.path),
); );
} }
...@@ -460,6 +476,7 @@ class ErrorHandlingDirectory ...@@ -460,6 +476,7 @@ class ErrorHandlingDirectory
platform: _platform, platform: _platform,
failureMessage: failureMessage:
'Flutter failed to delete a directory at "${delegate.path}"', 'Flutter failed to delete a directory at "${delegate.path}"',
posixPermissionSuggestion: recursive ? null : _posixPermissionSuggestion(delegate.path),
); );
} }
...@@ -470,6 +487,7 @@ class ErrorHandlingDirectory ...@@ -470,6 +487,7 @@ class ErrorHandlingDirectory
platform: _platform, platform: _platform,
failureMessage: failureMessage:
'Flutter failed to delete a directory at "${delegate.path}"', 'Flutter failed to delete a directory at "${delegate.path}"',
posixPermissionSuggestion: recursive ? null : _posixPermissionSuggestion(delegate.path),
); );
} }
...@@ -480,9 +498,13 @@ class ErrorHandlingDirectory ...@@ -480,9 +498,13 @@ class ErrorHandlingDirectory
platform: _platform, platform: _platform,
failureMessage: failureMessage:
'Flutter failed to check for directory existence at "${delegate.path}"', 'Flutter failed to check for directory existence at "${delegate.path}"',
posixPermissionSuggestion: _posixPermissionSuggestion(delegate.parent.path),
); );
} }
String _posixPermissionSuggestion(String path) => 'Try running:\n'
' sudo chown -R \$(whoami) ${fileSystem.path.absolute(path)}';
@override @override
String toString() => delegate.toString(); String toString() => delegate.toString();
} }
...@@ -538,6 +560,7 @@ const String _kNoExecutableFound = 'The Flutter tool could not locate an executa ...@@ -538,6 +560,7 @@ const String _kNoExecutableFound = 'The Flutter tool could not locate an executa
Future<T> _run<T>(Future<T> Function() op, { Future<T> _run<T>(Future<T> Function() op, {
required Platform platform, required Platform platform,
String? failureMessage, String? failureMessage,
String? posixPermissionSuggestion,
}) async { }) async {
assert(platform != null); assert(platform != null);
try { try {
...@@ -551,14 +574,14 @@ Future<T> _run<T>(Future<T> Function() op, { ...@@ -551,14 +574,14 @@ Future<T> _run<T>(Future<T> Function() op, {
if (platform.isWindows) { if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0); _handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
} else if (platform.isLinux || platform.isMacOS) { } else if (platform.isLinux || platform.isMacOS) {
_handlePosixException(e, failureMessage, e.osError?.errorCode ?? 0); _handlePosixException(e, failureMessage, e.osError?.errorCode ?? 0, posixPermissionSuggestion);
} }
rethrow; rethrow;
} on io.ProcessException catch (e) { } on io.ProcessException catch (e) {
if (platform.isWindows) { if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.errorCode); _handleWindowsException(e, failureMessage, e.errorCode);
} else if (platform.isLinux || platform.isMacOS) { } else if (platform.isLinux || platform.isMacOS) {
_handlePosixException(e, failureMessage, e.errorCode); _handlePosixException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
} }
rethrow; rethrow;
} }
...@@ -567,6 +590,7 @@ Future<T> _run<T>(Future<T> Function() op, { ...@@ -567,6 +590,7 @@ Future<T> _run<T>(Future<T> Function() op, {
T _runSync<T>(T Function() op, { T _runSync<T>(T Function() op, {
required Platform platform, required Platform platform,
String? failureMessage, String? failureMessage,
String? posixPermissionSuggestion,
}) { }) {
assert(platform != null); assert(platform != null);
try { try {
...@@ -580,14 +604,14 @@ T _runSync<T>(T Function() op, { ...@@ -580,14 +604,14 @@ T _runSync<T>(T Function() op, {
if (platform.isWindows) { if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0); _handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
} else if (platform.isLinux || platform.isMacOS) { } else if (platform.isLinux || platform.isMacOS) {
_handlePosixException(e, failureMessage, e.osError?.errorCode ?? 0); _handlePosixException(e, failureMessage, e.osError?.errorCode ?? 0, posixPermissionSuggestion);
} }
rethrow; rethrow;
} on io.ProcessException catch (e) { } on io.ProcessException catch (e) {
if (platform.isWindows) { if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.errorCode); _handleWindowsException(e, failureMessage, e.errorCode);
} else if (platform.isLinux || platform.isMacOS) { } else if (platform.isLinux || platform.isMacOS) {
_handlePosixException(e, failureMessage, e.errorCode); _handlePosixException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
} }
rethrow; rethrow;
} }
...@@ -617,6 +641,9 @@ class ErrorHandlingProcessManager extends ProcessManager { ...@@ -617,6 +641,9 @@ class ErrorHandlingProcessManager extends ProcessManager {
return _runSync( return _runSync(
() => _delegate.canRun(executable, workingDirectory: workingDirectory), () => _delegate.canRun(executable, workingDirectory: workingDirectory),
platform: _platform, platform: _platform,
failureMessage: 'Flutter failed to run "$executable"',
posixPermissionSuggestion: 'Try running:\n'
' sudo chown -R \$(whoami) $executable && chmod u+rx $executable',
); );
} }
...@@ -695,7 +722,7 @@ class ErrorHandlingProcessManager extends ProcessManager { ...@@ -695,7 +722,7 @@ class ErrorHandlingProcessManager extends ProcessManager {
} }
} }
void _handlePosixException(Exception e, String? message, int errorCode) { void _handlePosixException(Exception e, String? message, int errorCode, String? posixPermissionSuggestion) {
// From: // From:
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h
...@@ -714,10 +741,18 @@ void _handlePosixException(Exception e, String? message, int errorCode) { ...@@ -714,10 +741,18 @@ void _handlePosixException(Exception e, String? message, int errorCode) {
break; break;
case eperm: case eperm:
case eacces: case eacces:
errorMessage = final StringBuffer errorBuffer = StringBuffer();
'$message. The flutter tool cannot access the file or directory.\n' if (message != null && message.isNotEmpty) {
'Please ensure that the SDK and/or project is installed in a location ' errorBuffer.writeln('$message.');
'that has read/write permissions for the current user.'; } else {
errorBuffer.writeln('The flutter tool cannot access the file or directory.');
}
errorBuffer.writeln('Please ensure that the SDK and/or project is installed in a location '
'that has read/write permissions for the current user.');
if (posixPermissionSuggestion != null && posixPermissionSuggestion.isNotEmpty) {
errorBuffer.writeln(posixPermissionSuggestion);
}
errorMessage = errorBuffer.toString();
break; break;
default: default:
// Caller must rethrow the exception. // Caller must rethrow the exception.
......
...@@ -104,8 +104,7 @@ void main() { ...@@ -104,8 +104,7 @@ void main() {
config = Config.createForTesting(file, bufferLogger); config = Config.createForTesting(file, bufferLogger);
expect(bufferLogger.errorText, contains('Could not read preferences in testfile')); expect(bufferLogger.errorText, contains('Could not read preferences in testfile'));
// Also contains original error message: expect(bufferLogger.errorText, contains(r'sudo chown -R $(whoami) /testfile'));
expect(bufferLogger.errorText, contains('The flutter tool cannot access the file or directory'));
}); });
testWithoutContext('Config in home dir is used if it exists', () { testWithoutContext('Config in home dir is used if it exists', () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment