Chromium Code Reviews| Index: sdk/lib/_internal/pub/lib/src/io.dart |
| diff --git a/sdk/lib/_internal/pub/lib/src/io.dart b/sdk/lib/_internal/pub/lib/src/io.dart |
| index 0e2a93210469b742a4a13a4b6a26178950f77f70..46e525509ca126309868b25d1211082c0c547eed 100644 |
| --- a/sdk/lib/_internal/pub/lib/src/io.dart |
| +++ b/sdk/lib/_internal/pub/lib/src/io.dart |
| @@ -6,6 +6,7 @@ |
| library pub.io; |
| import 'dart:async'; |
| +import 'dart:collection'; |
| import 'dart:io'; |
| import 'dart:isolate'; |
| import 'dart:json'; |
| @@ -48,12 +49,74 @@ bool fileExists(String file) => new File(file).existsSync(); |
| /// filesystem; nonexistent or unreadable path entries are treated as normal |
| /// directories. |
| String canonicalize(String pathString) { |
| - var components = path.split(path.normalize(path.absolute(pathString))); |
| - var newPath = components.removeAt(0); |
| - // Rebuild the path one level at a time, resolving the symlinks for each level |
| - // in turn. |
| - for (var component in components) { |
| - newPath = resolveLink(path.join(newPath, component)); |
| + var seen = new Set<String>(); |
|
Bob Nystrom
2013/05/07 23:16:36
Wow. Nasty but at least it's very well documented.
nweiz
2013/05/07 23:19:14
I thought about that, but right now pathos is very
|
| + var components = new Queue<String>.from( |
| + path.split(path.normalize(path.absolute(pathString)))); |
| + |
| + // The canonical path, built incrementally as we iterate through [components]. |
| + var newPath = components.removeFirst(); |
| + |
| + // Move through the components of the path, resolving each one's symlinks as |
| + // necessary. A resolved component may also add new components that need to be |
| + // resolved in turn. |
| + while (!components.isEmpty) { |
| + seen.add(path.join(newPath, path.joinAll(components))); |
| + var resolvedPath = resolveLink( |
| + path.join(newPath, components.removeFirst())); |
| + var relative = path.relative(resolvedPath, from: newPath); |
| + |
| + // If the resolved path of the component relative to `newPath` is just ".", |
| + // that means component was a symlink pointing to its parent directory. We |
| + // can safely ignore such components. |
| + if (relative == '.') continue; |
| + |
| + var relativeComponents = new Queue<String>.from(path.split(relative)); |
| + |
| + // If the resolved path is absolute relative to `newPath`, that means it's |
| + // on a different drive. We need to canonicalize the entire target of that |
| + // symlink again. |
| + if (path.isAbsolute(relative)) { |
| + // If we've already tried to canonicalize the new path, we've encountered |
| + // a symlink loop. Avoid going infinite by treating the recursive symlink |
| + // as the canonical path. |
| + if (seen.contains(relative)) { |
| + newPath = relative; |
| + } else { |
| + newPath = relativeComponents.removeFirst(); |
| + relativeComponents.addAll(components); |
| + components = relativeComponents; |
| + } |
| + continue; |
| + } |
| + |
| + // Pop directories off `newPath` if the component links upwards in the |
| + // directory hierarchy. |
| + while (relativeComponents.first == '..') { |
| + newPath = path.dirname(newPath); |
| + relativeComponents.removeFirst(); |
| + } |
| + |
| + // If there's only one component left, [resolveLink] guarantees that it's |
| + // not a link (or is a broken link). We can just add it to `newPath` and |
| + // continue resolving the remaining components. |
| + if (relativeComponents.length == 1) { |
| + newPath = path.join(newPath, relativeComponents.single); |
| + continue; |
| + } |
| + |
| + // If we've already tried to canonicalize the new path, we've encountered a |
| + // symlink loop. Avoid going infinite by treating the recursive symlink as |
| + // the canonical path. |
| + var newSubPath = path.join(newPath, path.joinAll(relativeComponents)); |
| + if (seen.contains(newSubPath)) { |
| + newPath = newSubPath; |
| + continue; |
| + } |
| + |
| + // If there are multiple new components to resolve, add them to the |
| + // beginning of the queue. |
|
Bob Nystrom
2013/05/07 23:16:36
beginning -> end
nweiz
2013/05/07 23:19:14
They're actually added to the beginning, since the
|
| + relativeComponents.addAll(components); |
| + components = relativeComponents; |
| } |
| return newPath; |
| } |