mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 15:17:07 +00:00
a7f2da3898
Change-Id: Ibbc692545af636e33cfa7f5946480d3640d95b5c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178985 Commit-Queue: Lasse R.H. Nielsen <lrn@google.com> Reviewed-by: Lasse R.H. Nielsen <lrn@google.com> Reviewed-by: Nate Bosch <nbosch@google.com>
284 lines
10 KiB
Dart
284 lines
10 KiB
Dart
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
part of dart.io;
|
|
|
|
/// References to filesystem links.
|
|
@pragma("vm:entry-point")
|
|
abstract class Link implements FileSystemEntity {
|
|
/// Creates a Link object.
|
|
@pragma("vm:entry-point")
|
|
factory Link(String path) {
|
|
final IOOverrides? overrides = IOOverrides.current;
|
|
if (overrides == null) {
|
|
return new _Link(path);
|
|
}
|
|
return overrides.createLink(path);
|
|
}
|
|
|
|
@pragma("vm:entry-point")
|
|
factory Link.fromRawPath(Uint8List rawPath) {
|
|
// TODO(bkonyi): handle overrides
|
|
return new _Link.fromRawPath(rawPath);
|
|
}
|
|
|
|
/// Creates a [Link] object.
|
|
///
|
|
/// If [path] is a relative path, it will be interpreted relative to the
|
|
/// current working directory (see [Directory.current]), when used.
|
|
///
|
|
/// If [path] is an absolute path, it will be immune to changes to the
|
|
/// current working directory.
|
|
factory Link.fromUri(Uri uri) => new Link(uri.toFilePath());
|
|
|
|
/// Creates a symbolic link in the file system.
|
|
///
|
|
/// Returns a `Future<Link>` that completes with
|
|
/// the link when it has been created. If the link path already exists,
|
|
/// the future will complete with an error.
|
|
///
|
|
/// If [recursive] is `false`, the default, the link is created
|
|
/// only if all directories in its path exist.
|
|
/// If [recursive] is `true`, all non-existing parent paths
|
|
/// are created first. The directories in the path of [target] are
|
|
/// not affected, unless they are also in [path].
|
|
///
|
|
/// On the Windows platform, this call will create a true symbolic link
|
|
/// instead of a Junction. In order to create a symbolic link on Windows, Dart
|
|
/// must be run in Administrator mode or the system must have Developer Mode
|
|
/// enabled, otherwise a [FileSystemException] will be raised with
|
|
/// `ERROR_PRIVILEGE_NOT_HELD` set as the errno when this call is made.
|
|
///
|
|
/// On other platforms, the POSIX `symlink()` call is used to make a symbolic
|
|
/// link containing the string [target]. If [target] is a relative path,
|
|
/// it will be interpreted relative to the directory containing the link.
|
|
Future<Link> create(String target, {bool recursive = false});
|
|
|
|
/// Synchronously create the link. Calling [createSync] on an existing link
|
|
/// will throw an exception.
|
|
///
|
|
/// If [recursive] is `false`, the default, the link is created only if all
|
|
/// directories in its path exist. If [recursive] is `true`, all
|
|
/// non-existing parent paths are created first. The directories in
|
|
/// the path of [target] are not affected, unless they are also in [path].
|
|
///
|
|
/// On the Windows platform, this call will create a true symbolic link
|
|
/// instead of a Junction. In order to create a symbolic link on Windows, Dart
|
|
/// must be run in Administrator mode or the system must have Developer Mode
|
|
/// enabled, otherwise a [FileSystemException] will be raised with
|
|
/// `ERROR_PRIVILEGE_NOT_HELD` set as the errno when this call is made.
|
|
///
|
|
/// On other platforms, the POSIX `symlink()` call is used to make a symbolic
|
|
/// link containing the string [target]. If [target] is a relative path,
|
|
/// it will be interpreted relative to the directory containing the link.
|
|
void createSync(String target, {bool recursive = false});
|
|
|
|
/// Synchronously updates the link.
|
|
///
|
|
/// Calling [updateSync] on a non-existing link will throw an exception.
|
|
void updateSync(String target);
|
|
|
|
/// Updates the link.
|
|
///
|
|
/// Returns a `Future<Link>` that completes with the
|
|
/// link when it has been updated. Calling [update] on a non-existing link
|
|
/// will complete its returned future with an exception.
|
|
Future<Link> update(String target);
|
|
|
|
Future<String> resolveSymbolicLinks();
|
|
|
|
String resolveSymbolicLinksSync();
|
|
|
|
/// Renames this link.
|
|
///
|
|
/// Returns a `Future<Link>` that completes with a [Link]
|
|
/// for the renamed link.
|
|
///
|
|
/// If [newPath] identifies an existing link, that link is
|
|
/// removed first. If [newPath] identifies an existing file or directory,
|
|
/// the operation fails and the future completes with an exception.
|
|
Future<Link> rename(String newPath);
|
|
|
|
/// Synchronously renames this link.
|
|
///
|
|
/// Returns a [Link] instance for the renamed link.
|
|
///
|
|
/// If [newPath] identifies an existing link, that link is
|
|
/// removed first. If [newPath] identifies an existing file or directory
|
|
/// the operation fails and an exception is thrown.
|
|
Link renameSync(String newPath);
|
|
|
|
/// A [Link] instance whose path is the absolute path to [this].
|
|
///
|
|
/// The absolute path is computed by prefixing
|
|
/// a relative path with the current working directory, or returning
|
|
/// an absolute path unchanged.
|
|
Link get absolute;
|
|
|
|
/// Gets the target of the link.
|
|
///
|
|
/// Returns a future that completes with the path to the target.
|
|
///
|
|
/// If the returned target is a relative path, it is relative to the
|
|
/// directory containing the link.
|
|
///
|
|
/// If the link does not exist, or is not a link, the future completes with
|
|
/// a [FileSystemException].
|
|
Future<String> target();
|
|
|
|
/// Synchronously gets the target of the link.
|
|
///
|
|
/// Returns the path to the target.
|
|
///
|
|
/// If the returned target is a relative path, it is relative to the
|
|
/// directory containing the link.
|
|
///
|
|
/// If the link does not exist, or is not a link,
|
|
/// throws a [FileSystemException].
|
|
String targetSync();
|
|
}
|
|
|
|
class _Link extends FileSystemEntity implements Link {
|
|
final String _path;
|
|
final Uint8List _rawPath;
|
|
|
|
_Link(String path)
|
|
: _path = path,
|
|
_rawPath = FileSystemEntity._toUtf8Array(path);
|
|
|
|
_Link.fromRawPath(Uint8List rawPath)
|
|
: _rawPath = FileSystemEntity._toNullTerminatedUtf8Array(rawPath),
|
|
_path = FileSystemEntity._toStringFromUtf8Array(rawPath);
|
|
|
|
String get path => _path;
|
|
|
|
String toString() => "Link: '$path'";
|
|
|
|
Future<bool> exists() => FileSystemEntity._isLinkRaw(_rawPath);
|
|
|
|
bool existsSync() => FileSystemEntity._isLinkRawSync(_rawPath);
|
|
|
|
Link get absolute => isAbsolute ? this : _Link(_absolutePath);
|
|
|
|
Future<Link> create(String target, {bool recursive = false}) {
|
|
var result =
|
|
recursive ? parent.create(recursive: true) : new Future.value(null);
|
|
return result
|
|
.then((_) => _File._dispatchWithNamespace(
|
|
_IOService.fileCreateLink, [null, _rawPath, target]))
|
|
.then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionFromResponse(
|
|
response, "Cannot create link to target '$target'", path);
|
|
}
|
|
return this;
|
|
});
|
|
}
|
|
|
|
void createSync(String target, {bool recursive = false}) {
|
|
if (recursive) {
|
|
parent.createSync(recursive: true);
|
|
}
|
|
var result = _File._createLink(_Namespace._namespace, _rawPath, target);
|
|
throwIfError(result, "Cannot create link", path);
|
|
}
|
|
|
|
void updateSync(String target) {
|
|
// TODO(12414): Replace with atomic update, where supported by platform.
|
|
// Atomically changing a link can be done by creating the new link, with
|
|
// a different name, and using the rename() posix call to move it to
|
|
// the old name atomically.
|
|
deleteSync();
|
|
createSync(target);
|
|
}
|
|
|
|
Future<Link> update(String target) {
|
|
// TODO(12414): Replace with atomic update, where supported by platform.
|
|
// Atomically changing a link can be done by creating the new link, with
|
|
// a different name, and using the rename() posix call to move it to
|
|
// the old name atomically.
|
|
return delete().then<Link>((_) => create(target));
|
|
}
|
|
|
|
Future<Link> _delete({bool recursive = false}) {
|
|
if (recursive) {
|
|
return new Directory.fromRawPath(_rawPath)
|
|
.delete(recursive: true)
|
|
.then((_) => this);
|
|
}
|
|
return _File._dispatchWithNamespace(
|
|
_IOService.fileDeleteLink, [null, _rawPath]).then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionFromResponse(response, "Cannot delete link", path);
|
|
}
|
|
return this;
|
|
});
|
|
}
|
|
|
|
void _deleteSync({bool recursive = false}) {
|
|
if (recursive) {
|
|
return new Directory.fromRawPath(_rawPath).deleteSync(recursive: true);
|
|
}
|
|
var result = _File._deleteLinkNative(_Namespace._namespace, _rawPath);
|
|
throwIfError(result, "Cannot delete link", path);
|
|
}
|
|
|
|
Future<Link> rename(String newPath) {
|
|
return _File._dispatchWithNamespace(
|
|
_IOService.fileRenameLink, [null, _rawPath, newPath]).then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionFromResponse(
|
|
response, "Cannot rename link to '$newPath'", path);
|
|
}
|
|
return new Link(newPath);
|
|
});
|
|
}
|
|
|
|
Link renameSync(String newPath) {
|
|
var result = _File._renameLink(_Namespace._namespace, _rawPath, newPath);
|
|
throwIfError(result, "Cannot rename link '$path' to '$newPath'");
|
|
return new Link(newPath);
|
|
}
|
|
|
|
Future<String> target() {
|
|
return _File._dispatchWithNamespace(
|
|
_IOService.fileLinkTarget, [null, _rawPath]).then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionFromResponse(
|
|
response, "Cannot get target of link", path);
|
|
}
|
|
return response;
|
|
});
|
|
}
|
|
|
|
String targetSync() {
|
|
var result = _File._linkTarget(_Namespace._namespace, _rawPath);
|
|
throwIfError(result, "Cannot read link", path);
|
|
return result;
|
|
}
|
|
|
|
static throwIfError(Object? result, String msg, [String path = ""]) {
|
|
if (result is OSError) {
|
|
throw new FileSystemException(msg, path, result);
|
|
}
|
|
}
|
|
|
|
bool _isErrorResponse(response) {
|
|
return response is List && response[0] != _successResponse;
|
|
}
|
|
|
|
_exceptionFromResponse(response, String message, String path) {
|
|
assert(_isErrorResponse(response));
|
|
switch (response[_errorResponseErrorType]) {
|
|
case _illegalArgumentResponse:
|
|
return new ArgumentError();
|
|
case _osErrorResponse:
|
|
var err = new OSError(response[_osErrorResponseMessage],
|
|
response[_osErrorResponseErrorCode]);
|
|
return new FileSystemException(message, path, err);
|
|
default:
|
|
return new Exception("Unknown error");
|
|
}
|
|
}
|
|
}
|