configparser: be more permissive about include paths

Summary:
Windows has 2 kinds of paths - the UNC path (starting with `\\?\`), and paths
most people use (ex. `C:\foo\bar`). The former is more powerful (reserved names
like `nul` can be used), and is the "canonicalized" form as seen by Rust stdlib.

The UNC paths are stricter, though. `/` is not treated as `\` automatically,
`.` and `..` are considered illegal. That is, trying to canonicalize a UNC path
with `..` or `.` will result in an error.

It's possible to get `.`, or `..` into part of an UNC path, by using the
`PathBuf::join` API provided by the Rust stdlib. That is, a legal UNC path stored
in `PathBuf` can become illegal by `join`ing a non-UNC path.

I'm not sure what's the most "clean" fix. Perhaps using two different types to
represent UNC path and non-UNC path in stdlib? But that's definitely not a trivial
(or even practical) change.

For now, just teach the config parser to "friendly try again" by stripping the
UNC prefix and re-canonicalize paths. So it can handle `.` or `..` used by
`%include`.

Reviewed By: sfilipco

Differential Revision: D14568119

fbshipit-source-id: 2a55faa945c8d03574fd56e82d946c9ef7f0138f
This commit is contained in:
Jun Wu 2019-03-22 11:30:05 -07:00 committed by Facebook Github Bot
parent 8021c26449
commit 5e2df5b977

View File

@ -228,6 +228,22 @@ impl ConfigSet {
}
Err(error) => errors.push(Error::Io(path.to_path_buf(), error)),
}
} else {
// On Windows, a UNC path `\\?\C:\foo\.\x` will fail to canonicalize
// because it contains `.`. That path can be constructed by using
// `PathBuf::join` to concatenate a UNC path `\\?\C:\foo` with
// a "normal" path `.\x`.
// Try to fix it automatically by stripping the UNC prefix and retry
// `canonicalize`. `C:\foo\.\x` would be canonicalized without errors.
#[cfg(windows)]
{
if let Some(path_str) = path.to_str() {
if path_str.starts_with(r"\\?\") {
let path = Path::new(&path_str[4..]);
self.load_file(&path, opts, visited, errors);
}
}
}
}
// If `path.canonicalize` reports an error. It's usually the path cannot