Skip to content

Commit

Permalink
Fix fs::canonicalize to work with broken drivers
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisDenton committed Aug 20, 2021
1 parent 5217347 commit d048712
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 3 deletions.
1 change: 1 addition & 0 deletions library/std/src/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ pub struct ipv6_mreq {
}

pub const VOLUME_NAME_DOS: DWORD = 0x0;
pub const VOLUME_NAME_NT: DWORD = 0x2;
pub const MOVEFILE_REPLACE_EXISTING: DWORD = 1;

pub const FILE_BEGIN: DWORD = 0;
Expand Down
33 changes: 30 additions & 3 deletions library/std/src/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,10 +865,37 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
}

fn get_path(f: &File) -> io::Result<PathBuf> {
get_path_with_flags(f, c::VOLUME_NAME_DOS).or_else(|error| {
if error.raw_os_error() == Some(c::ERROR_INVALID_FUNCTION as i32) {
// This should normally be unreachable. See comment below.
get_path_fallback(f)
} else {
Err(error)
}
})
}

// Unfortunately some third party filesystem drivers don't implement the
// volume manager interface that the kernel requires. This breaks a number of
// things, including resolving the win32 path from a file handle.
//
// To workaround these broken drivers, this function instead gets the NT kernel
// path (which is always available) and then uses the magic win32 prefix
// `\\?\GLOBALROOT` so that the NT path can be used in win32 code.
//
// A downside to this is that it produces weird paths that users may find
// strange.
fn get_path_fallback(f: &File) -> io::Result<PathBuf> {
get_path_with_flags(f, c::VOLUME_NAME_NT).map(|nt_path| {
let mut win32_path: OsString = r"\\?\GLOBALROOT".into();
win32_path.push(nt_path);
win32_path.into()
})
}

fn get_path_with_flags(f: &File, flags: u32) -> io::Result<PathBuf> {
super::fill_utf16_buf(
|buf, sz| unsafe {
c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
},
|buf, sz| unsafe { c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, flags) },
|buf| PathBuf::from(OsString::from_wide(buf)),
)
}
Expand Down

0 comments on commit d048712

Please sign in to comment.