-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Windows: success of canonicalizing r"\" depends on whether set_current_dir has been called #49342
Comments
Personally I'd argue that we shouldn't try to "fix" the behavior from the OS with regards to canonicalization. It works as intended, which is to open a file or folder and then ask the OS what the canonical path to it is. What you might actually want is to simply call |
Thanks for the advice! I won't argue with what the rust core team feels is the right behavior. I agree that doing workarounds of poor OS design is not ideal, but sometimes that's what you would expect a standard library to do so shrug. Do you mean |
No, I mean |
I spent some time trying to reproduce this issue (from the description above and using the code that originally exhibited the problem; in a Win10 VM and using the AppVeyor configuration where the problem was originally observed). In all cases, the test passed and I could not reproduce the problem. |
I take it back, I finally figured out what's going on. I wrote a program that, for various paths A and B, would
From these results, I generalize:
Also, if the current directory is a UNC path ( None of this is mentioned in the SetCurrentDirectory docs. I'm not entirely sure what to suggest here. As @retep998 suggests, we probably shouldn't "fix" the OS behaviour with respect to canonicalization, but it'd be nice to mention it in the docs somewhere and save future generations the headache I've suffered. On the other hand, if it's too obscure to show up in the platform-specific MSDN docs, it's hard to justify mentioning it in the cross-platform Rust docs. |
Wow that table is awesome! I'm glad someone was able to figure out what is
happening... We can probably even write tests of what is expected now :)
|
Triage; looks like @rust-lang/libs would have to make some sort of decision here. |
As I am not very familiar with the intricate details of Windows paths, I am inclined to defer to @retep998 for a decision. |
I'd note that the Windows NT kernel does not really have the same concept of relative paths. All paths are absolute*. Interpreting relative paths happens in the Win32 user space. The Win32 API will call However, the problem comes when you set the current directory to an NT path (meaning Win32 should bypass resolving path) but use a relative path when opening a file (meaning Win32 should resolve the relative path before opening). I'm pretty sure this is undefined behaviour in the sense that this is not an expected situation. So it could produce weird paths which aren't openable due to not existing. I'd assume that future updates of Windows 10 could better account for this scenario but it's likely always going to be a weird mix. For what it's worth, my take is the documentation should be updated to advise against using |
An alternative would be for |
Silently stripping the prefix probably isn't a good idea, since there's a variety of perfectly sensible paths that will break in confusing ways if Win32 is allowed to mess with them (paths longer than 256 characters, for example, or paths containing the segment I would support updating the |
Sorry, I was not suggesting ignoring the prefix in the general case. Only when calling fn chdir(p: &path::Path) -> io::Result<()> {
let p: &OsStr = p.as_ref();
// Skip the `\\?\` verbatim prefix, if any.
static PREFIX: &[u8] = br"\\?\";
let bytes = os_str_as_u8_slice(p);
let prefix_len = if bytes.starts_with(PREFIX) {
PREFIX.len()
} else {
0
};
let mut p = p.encode_wide().skip(prefix_len).collect::<Vec<_>>();
p.push(0);
cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
} However, returning an error may well be a better option. Though my hunch is most people who call |
So to summarise: if the path contains a verbatim prefix
IMHO, whatever the choice the behaviour should be explicitly documented. |
The following is my conclusions based on testing in Windows 10 version 1909.
I'm not sure whether this is too platform specific for the Rust docs or if something like the above (minus my commentary) would be a beneficial addition. I think the biggest issues is, as in the original report, using That specific issue could also be side-stepped by automatically removing the prefix in the case of drive and UNC paths. We already have std::path::Prefix for detecting the type of path. So it just requires special casing |
I could more succinctly summarise the above as: Relative paths are not |
Problem: sometimes `Path::new(r"").canonicalize() fails
Note: apparently this isn't necessarily against the spec (a better link to the windows spec would be nice)
Regardless of whether it is against the spec or not, I think basing the "root location" off of the
current_dir
is a best practice. At the very least, havingcanonicalize(r"\")
fail is unexpected.Full Report
I hit this problem during one of my tests for path_abs. The way I handle it is thorny (and there is still a TODO hidden in there).
The below test passes on windows (and linux obviously):
The way I handle this in
path_abs
is the following:Root
current_dir
and use it's root instead.That code is here (it's messy and tied to resolving the absolute path in general).
Rust Info
Running in Windows Appveyor: https://ci.appveyor.com/project/vitiral/path-abs
The text was updated successfully, but these errors were encountered: