1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 08:19:13 +00:00

[io/file] - add FileSystemNotFoundException

Thrown by when an operation fails because a file is not found.

TEST=updated unit tests
Issue: https://github.com/dart-lang/sdk/issues/12461
Change-Id: I2e6e3986f92d5bf9f3922f4e2c6bbba67cc102bc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/267280
Reviewed-by: Lasse Nielsen <lrn@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Brian Quinlan <bquinlan@google.com>
This commit is contained in:
Brian Quinlan 2022-11-08 19:26:19 +00:00 committed by Commit Queue
parent 968c8210a2
commit b75f8aaaf5
22 changed files with 139 additions and 97 deletions

View File

@ -113,6 +113,12 @@
[#49878]: https://github.com/dart-lang/sdk/issues/49878
- When a `dart:io` operation fails because a file is not found, throw
`PathNotFoundException`, a `FileSystemException` subclass, to make it
easier to handle "file not found" errors.
[#12461]: https://github.com/dart-lang/sdk/issues/12461
#### `dart:isolate`
- Add `Isolate.run` to run a function in a new isolate.

View File

@ -571,13 +571,13 @@ bool SyncDirectoryListing::HandleFile(const char* file_name) {
bool SyncDirectoryListing::HandleError() {
Dart_Handle dart_os_error = DartUtils::NewDartOSError();
Dart_Handle args[3];
args[0] = DartUtils::NewString("Directory listing failed");
args[1] = DartUtils::NewString(error() ? "Invalid path" : CurrentPath());
args[2] = dart_os_error;
args[0] = dart_os_error;
args[1] = DartUtils::NewString("Directory listing failed");
args[2] = DartUtils::NewString(error() ? "Invalid path" : CurrentPath());
dart_error_ = Dart_New(
DartUtils::GetDartType(DartUtils::kIOLibURL, "FileSystemException"),
Dart_Null(), 3, args);
DartUtils::NewString("_fromOSError"), 3, args);
return false;
}

View File

@ -74,7 +74,7 @@ var tests = <VMTest>[
} on ServerRpcException catch (e) {
caughtException = true;
expect(e.code, equals(ServerRpcException.kFileDoesNotExist));
expect(e.message, startsWith("_readDevFSFile: FileSystemException: "));
expect(e.message, startsWith("_readDevFSFile: PathNotFoundException: "));
}
expect(caughtException, isTrue);

View File

@ -74,7 +74,7 @@ var tests = <VMTest>[
} on ServerRpcException catch (e) {
caughtException = true;
expect(e.code, equals(ServerRpcException.kFileDoesNotExist));
expect(e.message, startsWith("_readDevFSFile: FileSystemException: "));
expect(e.message, startsWith("_readDevFSFile: PathNotFoundException: "));
}
expect(caughtException, isTrue);

View File

@ -172,8 +172,8 @@ abstract class _FileSystemWatcher {
_id = _initWatcher();
_newWatcher();
} on dynamic catch (e) {
_broadcastController.addError(new FileSystemException(
"Failed to initialize file system entity watcher", null, e));
_broadcastController.addError(FileSystemException._fromOSError(
e, "Failed to initialize file system entity watcher", null));
_broadcastController.close();
return;
}
@ -183,8 +183,8 @@ abstract class _FileSystemWatcher {
pathId =
_watchPath(_id!, _Namespace._namespace, _path, _events, _recursive);
} on dynamic catch (e) {
_broadcastController
.addError(new FileSystemException("Failed to watch path", _path, e));
_broadcastController.addError(
FileSystemException._fromOSError(e, "Failed to watch path", _path));
_broadcastController.close();
return;
}

View File

@ -15,6 +15,21 @@ const int _errorResponseErrorType = 0;
const int _osErrorResponseErrorCode = 1;
const int _osErrorResponseMessage = 2;
// POSIX error codes.
// See https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
const _eNoEnt = 2;
// Windows error codes.
// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
const _errorFileNotFound = 2;
const _errorPathNotFound = 3;
const _errorInvalidDrive = 15;
const _errorNoMoreFiles = 18;
const _errorBadNetpath = 53;
const _errorBadNetName = 67;
const _errorBadPathName = 161;
const _errorFilenameExedRange = 206;
/// If the [response] is an error, throws an [Exception] or an [Error].
void _checkForErrorResponse(Object? response, String message, String path) {
if (response is List<Object?> && response[0] != _successResponse) {
@ -24,7 +39,7 @@ void _checkForErrorResponse(Object? response, String message, String path) {
case _osErrorResponse:
var err = OSError(response[_osErrorResponseMessage] as String,
response[_osErrorResponseErrorCode] as int);
throw FileSystemException(message, path, err);
throw FileSystemException._fromOSError(err, message, path);
case _fileClosedResponse:
throw FileSystemException("File closed", path);
default:
@ -64,12 +79,18 @@ class OSError implements Exception {
StringBuffer sb = new StringBuffer();
sb.write("OS Error");
if (message.isNotEmpty) {
sb..write(": ")..write(message);
sb
..write(": ")
..write(message);
if (errorCode != noErrorCode) {
sb..write(", errno = ")..write(errorCode.toString());
sb
..write(", errno = ")
..write(errorCode.toString());
}
} else if (errorCode != noErrorCode) {
sb..write(": errno = ")..write(errorCode.toString());
sb
..write(": errno = ")
..write(errorCode.toString());
}
return sb.toString();
}
@ -85,8 +106,7 @@ class _BufferAndStart {
// Ensure that the input List can be serialized through a native port.
_BufferAndStart _ensureFastAndSerializableByteData(
List<int> buffer, int start, int end) {
if ((buffer is Uint8List) &&
(buffer.buffer.lengthInBytes == buffer.length)) {
if ((buffer is Uint8List) && (buffer.buffer.lengthInBytes == buffer.length)) {
// Send typed data directly, unless it is a partial view, in which case we
// would rather copy than drag in the potentially much large backing store.
// See issue 50206.

View File

@ -39,8 +39,8 @@ class _Directory extends FileSystemEntity implements Directory {
static Directory get current {
var result = _current(_Namespace._namespace);
if (result is OSError) {
throw new FileSystemException(
"Getting current working directory failed", "", result);
throw FileSystemException._fromOSError(
result, "Getting current working directory failed", "");
}
return new _Directory(result);
}
@ -69,8 +69,8 @@ class _Directory extends FileSystemEntity implements Directory {
var result = _setCurrent(_Namespace._namespace, _rawPath);
if (result is ArgumentError) throw result;
if (result is OSError) {
throw new FileSystemException(
"Setting current working directory failed", path.toString(), result);
throw FileSystemException._fromOSError(
result, "Setting current working directory failed", path.toString());
}
}
@ -81,7 +81,7 @@ class _Directory extends FileSystemEntity implements Directory {
Future<bool> exists() {
return _File._dispatchWithNamespace(
_IOService.directoryExists, [null, _rawPath]).then((response) {
_checkForErrorResponse(response, "Exists failed");
_checkForErrorResponse(response, "Exists failed", path);
return response == 1;
});
}
@ -111,7 +111,7 @@ class _Directory extends FileSystemEntity implements Directory {
} else {
return _File._dispatchWithNamespace(
_IOService.directoryCreate, [null, _rawPath]).then((response) {
_checkForErrorResponse(response, "Creation failed");
_checkForErrorResponse(response, "Creation failed", path);
return this;
});
}
@ -126,7 +126,7 @@ class _Directory extends FileSystemEntity implements Directory {
}
var result = _create(_Namespace._namespace, _rawPath);
if (result is OSError) {
throw new FileSystemException("Creation failed", path, result);
throw FileSystemException._fromOSError(result, "Creation failed", path);
}
}
@ -150,7 +150,7 @@ class _Directory extends FileSystemEntity implements Directory {
return _File._dispatchWithNamespace(_IOService.directoryCreateTemp,
[null, FileSystemEntity._toUtf8Array(fullPrefix)]).then((response) {
_checkForErrorResponse(
response, "Creation of temporary directory failed");
response, "Creation of temporary directory failed", path);
return Directory(response as String);
});
}
@ -172,8 +172,8 @@ class _Directory extends FileSystemEntity implements Directory {
var result = _createTemp(
_Namespace._namespace, FileSystemEntity._toUtf8Array(fullPrefix));
if (result is OSError) {
throw new FileSystemException(
"Creation of temporary directory failed", fullPrefix, result);
throw new FileSystemException._fromOSError(
result, "Creation of temporary directory failed", fullPrefix);
}
return new Directory(result);
}
@ -182,7 +182,7 @@ class _Directory extends FileSystemEntity implements Directory {
return _File._dispatchWithNamespace(
_IOService.directoryDelete, [null, _rawPath, recursive])
.then((response) {
_checkForErrorResponse(response, "Deletion failed");
_checkForErrorResponse(response, "Deletion failed", path);
return this;
});
}
@ -190,14 +190,14 @@ class _Directory extends FileSystemEntity implements Directory {
void _deleteSync({bool recursive = false}) {
var result = _deleteNative(_Namespace._namespace, _rawPath, recursive);
if (result is OSError) {
throw new FileSystemException("Deletion failed", path, result);
throw FileSystemException._fromOSError(result, "Deletion failed", path);
}
}
Future<Directory> rename(String newPath) {
return _File._dispatchWithNamespace(
_IOService.directoryRename, [null, _rawPath, newPath]).then((response) {
_checkForErrorResponse(response, "Rename failed");
_checkForErrorResponse(response, "Rename failed", path);
return new Directory(newPath);
});
}
@ -207,7 +207,7 @@ class _Directory extends FileSystemEntity implements Directory {
ArgumentError.checkNotNull(newPath, "newPath");
var result = _rename(_Namespace._namespace, _rawPath, newPath);
if (result is OSError) {
throw new FileSystemException("Rename failed", path, result);
throw FileSystemException._fromOSError(result, "Rename failed", path);
}
return new Directory(newPath);
}
@ -244,22 +244,6 @@ class _Directory extends FileSystemEntity implements Directory {
String toString() => "Directory: '$path'";
/// If the [response] is an error, throws an [Exception] or an [Error].
void _checkForErrorResponse(Object? response, String message) {
if (response is List<Object?> && response[0] != _successResponse) {
switch (response[_errorResponseErrorType]) {
case _illegalArgumentResponse:
throw ArgumentError();
case _osErrorResponse:
var err = OSError(response[_osErrorResponseMessage] as String,
response[_osErrorResponseErrorCode] as int);
throw FileSystemException(message, path, err);
default:
throw AssertionError("Unknown error");
}
}
}
// TODO(40614): Remove once non-nullability is sound.
static T _checkNotNull<T>(T t, String name) {
ArgumentError.checkNotNull(t, name);
@ -430,8 +414,8 @@ class _AsyncDirectoryLister {
} else if (errorPath is Uint8List) {
errorPath = utf8.decode(errorPath, allowMalformed: true);
}
controller.addError(new FileSystemException(
"Directory listing failed", errorPath as String, err));
controller.addError(FileSystemException._fromOSError(
err, "Directory listing failed", errorPath as String));
} else {
controller.addError(new FileSystemException("Internal error"));
}

View File

@ -418,6 +418,8 @@ abstract class File implements FileSystemEntity {
///
/// * [FileMode.append]: same as [FileMode.write] except that the file is
/// not truncated.
///
/// Throws a [FileSystemException] if the operation fails.
Future<RandomAccessFile> open({FileMode mode = FileMode.read});
/// Synchronously opens the file for random access operations.
@ -890,12 +892,42 @@ class FileSystemException implements IOException {
///
/// The [message] and [path] path defaults to empty strings if omitted,
/// and [osError] defaults to `null`.
@pragma("vm:entry-point")
const FileSystemException([this.message = "", this.path = "", this.osError]);
String toString() {
/// Create a new file system exception based on an [OSError.errorCode].
///
/// For example, if `errorCode == 2` then a [PathNotFoundException]
/// will be returned.
@pragma("vm:entry-point")
factory FileSystemException._fromOSError(
OSError err, String message, String? path) {
if (Platform.isWindows) {
switch (err.errorCode) {
case _errorFileNotFound:
case _errorPathNotFound:
case _errorInvalidDrive:
case _errorNoMoreFiles:
case _errorBadNetpath:
case _errorBadNetName:
case _errorBadPathName:
case _errorFilenameExedRange:
return PathNotFoundException(path!, err, message);
default:
return FileSystemException(message, path, err);
}
} else {
switch (err.errorCode) {
case _eNoEnt:
return PathNotFoundException(path!, err, message);
default:
return FileSystemException(message, path, err);
}
}
}
String _toStringHelper(String className) {
StringBuffer sb = new StringBuffer();
sb.write("FileSystemException");
sb.write(className);
if (message.isNotEmpty) {
sb.write(": $message");
if (path != null) {
@ -914,6 +946,22 @@ class FileSystemException implements IOException {
}
return sb.toString();
}
String toString() {
return _toStringHelper("FileSystemException");
}
}
/// Exception thrown when a file operation fails because a file or
/// directory does not exist.
class PathNotFoundException extends FileSystemException {
const PathNotFoundException(String path, OSError osError,
[String message = ""])
: super(message, path, osError);
String toString() {
return _toStringHelper("PathNotFoundException");
}
}
/// The "read" end of an [Pipe] created by [Pipe.create].

View File

@ -626,7 +626,7 @@ class _File extends FileSystemEntity implements File {
static throwIfError(Object result, String msg, String path) {
if (result is OSError) {
throw new FileSystemException(msg, path, result);
throw FileSystemException._fromOSError(result, msg, path);
}
}

View File

@ -251,23 +251,7 @@ class _Link extends FileSystemEntity implements Link {
static throwIfError(Object? result, String msg, [String path = ""]) {
if (result is OSError) {
throw new FileSystemException(msg, path, result);
}
}
/// If the [response] is an error, throws an [Exception] or an [Error].
void _checkForErrorResponse(Object? response, String message, String path) {
if (response is List<Object?> && response[0] != _successResponse) {
switch (response[_errorResponseErrorType]) {
case _illegalArgumentResponse:
throw ArgumentError();
case _osErrorResponse:
var err = OSError(response[_osErrorResponseMessage] as String,
response[_osErrorResponseErrorCode] as int);
throw FileSystemException(message, path, err);
default:
throw AssertionError("Unknown error");
}
throw FileSystemException._fromOSError(result, msg, path);
}
}
}

View File

@ -15,7 +15,7 @@ Directory tempDir() {
}
bool checkCreateInNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Creation failed") != -1);
if (Platform.operatingSystem == "linux") {
@ -41,7 +41,7 @@ void testCreateInNonExistent(Directory temp, Function done) {
}
bool checkCreateTempInNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
if (Platform.operatingSystem == "linux") {
Expect.equals(2, e.osError.errorCode);
@ -67,7 +67,7 @@ void testCreateTempInNonExistent(Directory temp, Function done) {
}
bool checkDeleteNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
// File not not found has error code 2 on all supported platforms.
Expect.equals(2, e.osError.errorCode);
@ -87,7 +87,7 @@ void testDeleteNonExistent(Directory temp, Function done) {
}
bool checkDeleteRecursivelyNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Deletion failed") != -1);
// File not not found has error code 2 on all supported platforms.
@ -109,7 +109,7 @@ void testDeleteRecursivelyNonExistent(Directory temp, Function done) {
}
bool checkListNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Directory listing failed") != -1);
if (Platform.operatingSystem == "linux") {
@ -141,12 +141,12 @@ void testRenameNonExistent(Directory temp, Function done) {
Directory nonExistent = new Directory("${temp.path}/nonExistent");
var newPath = "${temp.path}/nonExistent2";
Expect.throws(
() => nonExistent.renameSync(newPath), (e) => e is FileSystemException);
() => nonExistent.renameSync(newPath), (e) => e is PathNotFoundException);
var renameDone = nonExistent.rename(newPath);
renameDone
.then((ignore) => Expect.fail('rename non existent'))
.catchError((error) {
Expect.isTrue(error is FileSystemException);
Expect.isTrue(error is PathNotFoundException);
done();
});
}

View File

@ -16,9 +16,9 @@ void testListNonExistent() {
asyncStart();
Directory.systemTemp.createTemp('dart_directory_list_nonexistent').then((d) {
d.delete().then((ignore) {
Expect.throws(() => d.listSync(), (e) => e is FileSystemException);
Expect.throws(() => d.listSync(), (e) => e is PathNotFoundException);
Expect.throws(
() => d.listSync(recursive: true), (e) => e is FileSystemException);
() => d.listSync(recursive: true), (e) => e is PathNotFoundException);
asyncEnd();
});
});

View File

@ -162,7 +162,7 @@ class DirectoryTest {
future.then((ignore) {
Expect.fail("Deletion of non-existing directory should fail");
}).catchError((error) {
Expect.isTrue(error is FileSystemException);
Expect.isTrue(error is PathNotFoundException);
});
}

View File

@ -15,7 +15,7 @@ Directory tempDir() {
}
bool checkCannotOpenFileException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Cannot open file") != -1);
if (Platform.operatingSystem == "linux") {
@ -29,7 +29,7 @@ bool checkCannotOpenFileException(e) {
}
bool checkNonExistentFileSystemException(e, str) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf(str) != -1);
// File not not found has error code 2 on all supported platforms.

View File

@ -311,7 +311,7 @@ void testWatchNonExisting() {
Expect.fail('unexpected error');
}, onError: (e) {
asyncEnd();
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
});
}

View File

@ -241,7 +241,7 @@ void testLinkErrorSync() {
Expect.throws(
() => new Link('some-dir-that-doent exist/some link file/bla/fisk')
.createSync('bla bla bla/b lalal/blfir/sdfred/es'),
(e) => e is FileSystemException);
(e) => e is PathNotFoundException);
}
checkExists(String filePath) => Expect.isTrue(new File(filePath).existsSync());

View File

@ -17,7 +17,7 @@ Directory tempDir() {
}
bool checkCreateInNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Creation failed") != -1);
if (Platform.operatingSystem == "linux") {
@ -43,7 +43,7 @@ void testCreateInNonExistent(Directory temp, Function done) {
}
bool checkCreateTempInNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
if (Platform.operatingSystem == "linux") {
Expect.equals(2, e.osError.errorCode);
@ -68,7 +68,7 @@ void testCreateTempInNonExistent(Directory temp, Function done) {
}
bool checkDeleteNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
// File not not found has error code 2 on all supported platforms.
Expect.equals(2, e.osError.errorCode);
@ -88,7 +88,7 @@ void testDeleteNonExistent(Directory temp, Function done) {
}
bool checkDeleteRecursivelyNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Deletion failed") != -1);
// File not not found has error code 2 on all supported platforms.
@ -109,7 +109,7 @@ void testDeleteRecursivelyNonExistent(Directory temp, Function done) {
}
bool checkListNonExistentFileSystemException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Directory listing failed") != -1);
if (Platform.operatingSystem == "linux") {
@ -141,12 +141,12 @@ void testRenameNonExistent(Directory temp, Function done) {
Directory nonExistent = new Directory("${temp.path}/nonExistent");
var newPath = "${temp.path}/nonExistent2";
Expect.throws(
() => nonExistent.renameSync(newPath), (e) => e is FileSystemException);
() => nonExistent.renameSync(newPath), (e) => e is PathNotFoundException);
var renameDone = nonExistent.rename(newPath);
renameDone
.then((ignore) => Expect.fail('rename non existent'))
.catchError((error) {
Expect.isTrue(error is FileSystemException);
Expect.isTrue(error is PathNotFoundException);
done();
});
}

View File

@ -18,9 +18,9 @@ void testListNonExistent() {
asyncStart();
Directory.systemTemp.createTemp('dart_directory_list_nonexistent').then((d) {
d.delete().then((ignore) {
Expect.throws(() => d.listSync(), (e) => e is FileSystemException);
Expect.throws(() => d.listSync(), (e) => e is PathNotFoundException);
Expect.throws(
() => d.listSync(recursive: true), (e) => e is FileSystemException);
() => d.listSync(recursive: true), (e) => e is PathNotFoundException);
asyncEnd();
});
});

View File

@ -164,7 +164,7 @@ class DirectoryTest {
future.then((ignore) {
Expect.fail("Deletion of non-existing directory should fail");
}).catchError((error) {
Expect.isTrue(error is FileSystemException);
Expect.isTrue(error is PathNotFoundException);
});
}

View File

@ -17,7 +17,7 @@ Directory tempDir() {
}
bool checkCannotOpenFileException(e) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf("Cannot open file") != -1);
if (Platform.operatingSystem == "linux") {
@ -31,7 +31,7 @@ bool checkCannotOpenFileException(e) {
}
bool checkNonExistentFileSystemException(e, str) {
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
Expect.isTrue(e.osError != null);
Expect.isTrue(e.toString().indexOf(str) != -1);
// File not not found has error code 2 on all supported platforms.

View File

@ -312,7 +312,7 @@ void testWatchNonExisting() {
Expect.fail('unexpected error');
}, onError: (e) {
asyncEnd();
Expect.isTrue(e is FileSystemException);
Expect.isTrue(e is PathNotFoundException);
});
}

View File

@ -243,7 +243,7 @@ void testLinkErrorSync() {
Expect.throws(
() => new Link('some-dir-that-doent exist/some link file/bla/fisk')
.createSync('bla bla bla/b lalal/blfir/sdfred/es'),
(e) => e is FileSystemException);
(e) => e is PathNotFoundException);
}
checkExists(String filePath) => Expect.isTrue(new File(filePath).existsSync());