Skip to content
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

verbatim path prefix not normalized correctly #6

Open
andrewbanchich opened this issue Mar 30, 2021 · 5 comments
Open

verbatim path prefix not normalized correctly #6

andrewbanchich opened this issue Mar 30, 2021 · 5 comments

Comments

@andrewbanchich
Copy link

On Windows, calling path.to_slash() on a verbatim path like:

\\?\C:\Users\andrew\foo

produces:

\\?\C:/Users/andrew/foo

when it should produce:

\\?/C:/Users/andrew/foo
@rhysd
Copy link
Owner

rhysd commented Apr 2, 2021

I tried following code on Windows

use std::path::Path;

fn main() {
    let p = Path::new(r#"\\?\C:\Users\andrew\foo"#);
    for c in p.components() {
        println!("{:?}", c);
    }
}

The output was

Prefix(PrefixComponent { raw: "\\\\?\\C:", parsed: VerbatimDisk(67) })
RootDir
Normal("Users")
Normal("andrew")
Normal("foo")

Since slash path is a path where / is used for path separator, only path separator of \ should be replaced with /. Is the third \ in \\?\C: a path separator? I'm not familiar with verbatim path prefix since I'm mainly not a Windows user, so I'll appreciate if you kindly share the documentation which indicates your suggestion is correct.

@andrewbanchich
Copy link
Author

I'm mainly not a Windows user

Me neither =) I just do a lot of work with Windows paths for work. Unix is so much simpler.

I'm not an expert at all, but here is an example program to test if Windows can actually access certain file path formats:

use std::path::Path;
use path_slash::PathExt;

fn main() {

	// normal path works
	let p0 = Path::new(r"C:\Users\andrew\Documents\foo\test.txt");

	// verbatim path works
	let p1 = Path::new(r"\\?\C:\Users\andrew\Documents\foo\test.txt");

	// verbatim unix does not work
	let p2 = Path::new(r"\\?\C:\Users\andrew\Documents\foo\test.txt").to_slash_lossy();
	let p2 = Path::new(&p2);
	
	// changing \\?\ to \\?/ works
	let p3 = Path::new(r"\\?/C:\Users\andrew\Documents\foo\test.txt").to_slash_lossy();
	let p3 = Path::new(&p3);

	let p4 = Path::new(r"\\?/UNC\DESKTOP-DRCNKJR\asdf\test.txt").to_slash_lossy();
	let p4 = Path::new(&p4);
	
	let paths = vec![p0, p1, p2, p3, p4];
	
	for (i, path) in paths.iter().enumerate() {
		match path.metadata() {
			Ok(_) => println!("p{} works! ({:?})", i, path),
			Err(e) => eprintln!("p{} doesn't work. {} ({:?})",i, e, path)
		};

		for c in path.components() {
			dbg!(c);
		}
	}

}

Output:

p0 works! ("C:\\Users\\andrew\\Documents\\foo\\test.txt")
[src\main.rs:32] c = Prefix(
    PrefixComponent {
        raw: "C:",
        parsed: Disk(
            67,
        ),
    },
)
[src\main.rs:32] c = RootDir
[src\main.rs:32] c = Normal(
    "Users",
)
[src\main.rs:32] c = Normal(
    "andrew",
)
[src\main.rs:32] c = Normal(
    "Documents",
)
[src\main.rs:32] c = Normal(
    "foo",
)
[src\main.rs:32] c = Normal(
    "test.txt",
)
p1 works! ("\\\\?\\C:\\Users\\andrew\\Documents\\foo\\test.txt")
[src\main.rs:32] c = Prefix(
    PrefixComponent {
        raw: "\\\\?\\C:",
        parsed: VerbatimDisk(
            67,
        ),
    },
)
[src\main.rs:32] c = RootDir
[src\main.rs:32] c = Normal(
    "Users",
)
[src\main.rs:32] c = Normal(
    "andrew",
)
[src\main.rs:32] c = Normal(
    "Documents",
)
[src\main.rs:32] c = Normal(
    "foo",
)
[src\main.rs:32] c = Normal(
    "test.txt",
)
p2 doesn't work. The system cannot find the file specified. (os error 2) ("\\\\?\\C:/Users/andrew/Documents/foo/test.txt")
[src\main.rs:32] c = Prefix(
    PrefixComponent {
        raw: "\\\\?\\C:/Users/andrew/Documents/foo/test.txt",
        parsed: Verbatim(
            "C:/Users/andrew/Documents/foo/test.txt",
        ),
    },
)
p3 works! ("\\\\?/C:/Users/andrew/Documents/foo/test.txt")
[src\main.rs:32] c = Prefix(
    PrefixComponent {
        raw: "\\\\?/C:",
        parsed: UNC(
            "?",
            "C:",
        ),
    },
)
[src\main.rs:32] c = RootDir
[src\main.rs:32] c = Normal(
    "Users",
)
[src\main.rs:32] c = Normal(
    "andrew",
)
[src\main.rs:32] c = Normal(
    "Documents",
)
[src\main.rs:32] c = Normal(
    "foo",
)
[src\main.rs:32] c = Normal(
    "test.txt",
)
p4 works! ("\\\\?/UNC/DESKTOP-DRCNKJR/asdf/test.txt")
[src\main.rs:32] c = Prefix(
    PrefixComponent {
        raw: "\\\\?/UNC",
        parsed: UNC(
            "?",
            "UNC",
        ),
    },
)
[src\main.rs:32] c = RootDir
[src\main.rs:32] c = Normal(
    "DESKTOP-DRCNKJR",
)
[src\main.rs:32] c = Normal(
    "asdf",
)
[src\main.rs:32] c = Normal(
    "test.txt",
)

@andrewbanchich
Copy link
Author

andrewbanchich commented Apr 2, 2021

The Windows documentation on verbatim / extended-length path prefixes says:

These prefixes are not used as part of the path itself. They indicate that the path should be passed to the system with minimal modification, which means that you cannot use forward slashes to represent path separators, or a period to represent the current directory, or double dots to represent the parent directory.

When it says you cannot use forward slashes to represent path separators, maybe it means specifically with \\?\ but you can use them with \\?/? I don't know. I haven't read about the latter anywhere, but I just discovered that does allow you to use forward slashes with verbatim paths. That being said, \\?/ prefixes with unix-style paths will not work for extended length paths, while \\?\ will.

So my thought is that since the official docs say unix-style paths are not valid with \\?\ anyway, and since that combination doesn't work with Windows, maybe path_slash should use \\?/ instead so that normalized verbatim paths work properly (just not for long paths).

@andrewbanchich
Copy link
Author

andrewbanchich commented Apr 18, 2021

Actually, thinking this through some more, Rust thought \\?/UNC/ was a UNC, not VerbatimUNC (which I understand since it uses forward slashes. Since it doesn't work for accessing extended-length paths, then I guess Windows doesn't consider it one either.

I guess the main thing I'm wondering for path_slash is since \\?\C:/Users/andrew/foo doesn't actually work for accessing the file, do you think it should, instead, produce some error type / not normalize verbatim paths at all since MS says you can't use forward slashes for them?

@explosion-mental
Copy link

Just stumble at this when looking for .to_slash functionality.

The UNC is discuss in a rust github issue rust-lang/rust#42869

They are working on making a 'friendly' fs::absolute function to avoid the UNC stuff since not even many windows app support it an prefer the old win32 (msdos¿) way of representing paths.

The workaround I use is simply use dunce. You could, for instance, use the funcs from that crate and then apply .to_slash().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants