Skip to content

Commit d048712

Browse files
committed
Fix fs::canonicalize to work with broken drivers
1 parent 5217347 commit d048712

File tree

2 files changed

+31
-3
lines changed

2 files changed

+31
-3
lines changed

library/std/src/sys/windows/c.rs

+1
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ pub struct ipv6_mreq {
229229
}
230230

231231
pub const VOLUME_NAME_DOS: DWORD = 0x0;
232+
pub const VOLUME_NAME_NT: DWORD = 0x2;
232233
pub const MOVEFILE_REPLACE_EXISTING: DWORD = 1;
233234

234235
pub const FILE_BEGIN: DWORD = 0;

library/std/src/sys/windows/fs.rs

+30-3
Original file line numberDiff line numberDiff line change
@@ -865,10 +865,37 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
865865
}
866866

867867
fn get_path(f: &File) -> io::Result<PathBuf> {
868+
get_path_with_flags(f, c::VOLUME_NAME_DOS).or_else(|error| {
869+
if error.raw_os_error() == Some(c::ERROR_INVALID_FUNCTION as i32) {
870+
// This should normally be unreachable. See comment below.
871+
get_path_fallback(f)
872+
} else {
873+
Err(error)
874+
}
875+
})
876+
}
877+
878+
// Unfortunately some third party filesystem drivers don't implement the
879+
// volume manager interface that the kernel requires. This breaks a number of
880+
// things, including resolving the win32 path from a file handle.
881+
//
882+
// To workaround these broken drivers, this function instead gets the NT kernel
883+
// path (which is always available) and then uses the magic win32 prefix
884+
// `\\?\GLOBALROOT` so that the NT path can be used in win32 code.
885+
//
886+
// A downside to this is that it produces weird paths that users may find
887+
// strange.
888+
fn get_path_fallback(f: &File) -> io::Result<PathBuf> {
889+
get_path_with_flags(f, c::VOLUME_NAME_NT).map(|nt_path| {
890+
let mut win32_path: OsString = r"\\?\GLOBALROOT".into();
891+
win32_path.push(nt_path);
892+
win32_path.into()
893+
})
894+
}
895+
896+
fn get_path_with_flags(f: &File, flags: u32) -> io::Result<PathBuf> {
868897
super::fill_utf16_buf(
869-
|buf, sz| unsafe {
870-
c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
871-
},
898+
|buf, sz| unsafe { c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, flags) },
872899
|buf| PathBuf::from(OsString::from_wide(buf)),
873900
)
874901
}

0 commit comments

Comments
 (0)