fix: replace std::fs::canonicalize with Simplified::simple_canonicaliz… (#6776)

## Summary
This PR addresses an issue on Windows where `std::fs::canonicalize` can
fail or panic when resolving paths on mapped network drives. By
replacing it with `Simplified::simple_canonicalize`, we aim to improve
the robustness and cross-platform compatibility of path resolution.

### Changes
* Updated `CANONICAL_CWD` in `path.rs` to use
`Simplified::simple_canonicalize` instead of `std::fs::canonicalize`.

### Why
* `std::fs::canonicalize` has known issues with resolving paths on
mapped network drives on Windows, which can lead to panics or incorrect
path resolution.
* `Simplified::simple_canonicalize` internally uses
`dunce::canonicalize`, which handles these cases more gracefully,
ensuring better stability and reliability.

## Test Plan
Since `simple_canonicalize` has already been tested in a prior PR, this
change is expected to work without introducing any new issues. No
additional tests are necessary beyond ensuring existing tests pass,
which will confirm the correctness of the change.
This commit is contained in:
Chao Ning 2024-09-01 18:59:15 +01:00 committed by GitHub
parent 736ccb950a
commit ae3f35cfe2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,5 +1,5 @@
use std::borrow::Cow;
use std::path::{Component, Path, PathBuf};
use std::path::{absolute, Component, Path, PathBuf};
use std::sync::LazyLock;
use either::Either;
@ -13,7 +13,7 @@ pub static CWD: LazyLock<PathBuf> =
pub static CANONICAL_CWD: LazyLock<PathBuf> = LazyLock::new(|| {
std::env::current_dir()
.expect("The current directory must exist")
.canonicalize()
.simple_canonicalize()
.expect("The current directory must be canonicalized")
});
@ -30,6 +30,8 @@ pub trait Simplified {
fn simplified_display(&self) -> impl std::fmt::Display;
/// Canonicalize a path without a `\\?\` prefix on Windows.
/// For a path that can't be canonicalized (e.g. on network drive or RAM drive on Windows),
/// this will return the absolute path if it exists.
fn simple_canonicalize(&self) -> std::io::Result<PathBuf>;
/// Render a [`Path`] for user-facing display.
@ -59,7 +61,21 @@ impl<T: AsRef<Path>> Simplified for T {
}
fn simple_canonicalize(&self) -> std::io::Result<PathBuf> {
dunce::canonicalize(self.as_ref())
match dunce::canonicalize(self.as_ref()) {
Ok(path) => Ok(path),
// On Windows, `dunce::canonicalize` can't canonicalize paths on network drives or RAM
// drives. In that case, fall back on `std::path::absolute`, but also verify that the
// path exists.
Err(e) if std::env::consts::OS != "windows" => Err(e),
_ => match absolute(self.as_ref()) {
Ok(path) if path.exists() => Ok(path),
Ok(_) => Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Path does not exist: {}", self.simplified_display()),
)),
Err(e) => Err(e),
},
}
}
fn user_display(&self) -> impl std::fmt::Display {