blob: 72059aff2b4d8d11f988515e9e0f243493599609 [file] [log] [blame]
// Copyright (c) 2017, 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 file.src.backends.memory;
/// Checks if `node.type` returns [io.FileSystemEntityType.FILE].
bool _isFile(_Node node) => node?.type == io.FileSystemEntityType.FILE;
/// Checks if `node.type` returns [io.FileSystemEntityType.DIRECTORY].
bool _isDirectory(_Node node) =>
node?.type == io.FileSystemEntityType.DIRECTORY;
/// Checks if `node.type` returns [io.FileSystemEntityType.LINK].
bool _isLink(_Node node) => node?.type == io.FileSystemEntityType.LINK;
/// Tells whether the specified path represents an absolute path.
bool _isAbsolute(String path) => path.startsWith(_separator);
/// Generates a path to use in error messages.
typedef dynamic _PathGenerator();
/// Validator function that is expected to throw a [FileSystemException] if
/// the node does not represent the type that is expected in any given context.
typedef void _TypeChecker(_Node node);
/// Throws a [io.FileSystemException] if [node] is null.
void _checkExists(_Node node, _PathGenerator path) {
if (node == null) {
throw common.noSuchFileOrDirectory(path());
}
}
/// Throws a [io.FileSystemException] if [node] is not a directory.
void _checkIsDir(_Node node, _PathGenerator path) {
if (!_isDirectory(node)) {
throw common.notADirectory(path());
}
}
/// Throws a [io.FileSystemException] if [expectedType] doesn't match
/// [actualType].
void _checkType(
FileSystemEntityType expectedType,
FileSystemEntityType actualType,
_PathGenerator path,
) {
if (expectedType != actualType) {
switch (expectedType) {
case FileSystemEntityType.DIRECTORY:
throw common.notADirectory(path());
case FileSystemEntityType.FILE:
assert(actualType == FileSystemEntityType.DIRECTORY);
throw common.isADirectory(path());
case FileSystemEntityType.LINK:
throw common.invalidArgument(path());
default:
// Should not happen
throw new AssertionError();
}
}
}
/// Tells if the specified file mode represents a write mode.
bool _isWriteMode(io.FileMode mode) =>
mode == io.FileMode.WRITE ||
mode == io.FileMode.APPEND ||
mode == io.FileMode.WRITE_ONLY ||
mode == io.FileMode.WRITE_ONLY_APPEND;
/// Returns a [_PathGenerator] that generates a subpath of the constituent
/// [parts] (from [start]..[end], inclusive).
_PathGenerator _subpath(List<String> parts, int start, int end) {
return () => parts.sublist(start, end + 1).join(_separator);
}
/// Tells whether the given string is empty.
bool _isEmpty(String str) => str.isEmpty;
/// Returns the node ultimately referred to by [link]. This will resolve
/// the link references (following chains of links as necessary) and return
/// the node at the end of the link chain.
///
/// If a loop in the link chain is found, this will throw a
/// [FileSystemException], calling [path] to generate the path.
///
/// If [ledger] is specified, the resolved path to the terminal node will be
/// appended to the ledger (or overwritten in the ledger if a link target
/// specified an absolute path). The path will not be normalized, meaning
/// `..` and `.` path segments may be present.
///
/// If [tailVisitor] is specified, it will be invoked for the tail element of
/// the last link in the symbolic link chain, and its return value will be the
/// return value of this method (thus allowing callers to create the entity
/// at the end of the chain on demand).
_Node _resolveLinks(
_LinkNode link,
_PathGenerator path, {
List<String> ledger,
_Node tailVisitor(_DirectoryNode parent, String childName, _Node child),
}) {
// Record a breadcrumb trail to guard against symlink loops.
Set<_LinkNode> breadcrumbs = new Set<_LinkNode>();
_Node node = link;
while (_isLink(node)) {
link = node;
if (!breadcrumbs.add(node)) {
throw common.tooManyLevelsOfSymbolicLinks(path());
}
if (ledger != null) {
if (_isAbsolute(link.target)) {
ledger.clear();
} else if (ledger.isNotEmpty) {
ledger.removeLast();
}
ledger.addAll(link.target.split(_separator));
}
node = link.getReferent(
tailVisitor: (_DirectoryNode parent, String childName, _Node child) {
if (tailVisitor != null && !_isLink(child)) {
// Only invoke [tailListener] on the final resolution pass.
child = tailVisitor(parent, childName, child);
}
return child;
},
);
}
return node;
}