Skip to content

Commit

Permalink
Avoid permissions errors in _realCasePath. (#1181)
Browse files Browse the repository at this point in the history
This catches permissions errors and treats them as indicating that the
path component we have so far is canonical. This also improves the
efficiency of case matching by caching results for higher directories.
  • Loading branch information
nex3 authored Jan 6, 2021
1 parent 006e6aa commit f24a2b9
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.32.1

* Don't emit permissions errors on Windows and OS X when trying to determine the
real case of path names.

## 1.32.0

* Deprecate passing non-`%` numbers as lightness and saturation to `hsl()`,
Expand Down
53 changes: 41 additions & 12 deletions lib/src/io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import 'io/interface.dart'
if (dart.library.io) 'io/vm.dart'
if (dart.library.js) 'io/node.dart';
import 'utils.dart';
import 'util/character.dart';

export 'io/interface.dart'
if (dart.library.io) 'io/vm.dart'
if (dart.library.js) 'io/node.dart';

/// A cache of return values for directories in [_realCasePath].
final _realCaseCache = <String, String>{};

/// Returns whether the current operating system might be case-insensitive.
///
/// We can't know for sure because different Mac OS systems are configured
Expand All @@ -35,18 +39,43 @@ String _realCasePath(String path) {

if (!_couldBeCaseInsensitive) return path;

var realCasePath = p.rootPrefix(path);
for (var component in p.split(path.substring(realCasePath.length))) {
var matches = listDir(realCasePath)
.where((realPath) => equalsIgnoreCase(p.basename(realPath), component))
.toList();

realCasePath = matches.length != 1
// If the file doesn't exist, or if there are multiple options (meaning
// the filesystem isn't actually case-insensitive), use `component` as-is.
? p.join(realCasePath, component)
: matches[0];
if (isWindows) {
// Drive names are *always* case-insensitive, so convert them to uppercase.
var prefix = p.rootPrefix(path);
if (prefix.isNotEmpty && isAlphabetic(prefix.codeUnitAt(0))) {
path = prefix.toUpperCase() + path.substring(prefix.length);
}
}

String helper(String path) {
var dirname = p.dirname(path);
if (dirname == path) return path;

return _realCaseCache.putIfAbsent(path, () {
var realDirname = helper(dirname);
var basename = p.basename(path);

try {
var matches = listDir(realDirname)
.where(
(realPath) => equalsIgnoreCase(p.basename(realPath), basename))
.toList();

return matches.length != 1
// If the file doesn't exist, or if there are multiple options (meaning
// the filesystem isn't actually case-insensitive), use `basename`
// as-is.
? p.join(realDirname, basename)
: matches[0];
} on FileSystemException catch (_) {
// If there's an error listing a directory, it's likely because we're
// trying to reach too far out of the current directory into something
// we don't have permissions for. In that case, just assume we have the
// real path.
return path;
}
});
}

return realCasePath;
return helper(path);
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: sass
version: 1.32.0
version: 1.32.1
description: A Sass implementation in Dart.
author: Sass Team
homepage: https://github.com/sass/dart-sass
Expand Down

0 comments on commit f24a2b9

Please sign in to comment.