Skip to content

Commit 3e2d606

Browse files
committed
Automatically convert paths to verbatim
This allows using longer paths for filesystem operations without the user needing to `canonicalize` or manually prefix paths. If the path is already verbatim than this has no effect.
1 parent cfff31b commit 3e2d606

File tree

4 files changed

+163
-13
lines changed

4 files changed

+163
-13
lines changed

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

+6
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,12 @@ extern "system" {
990990
cchCount2: c_int,
991991
bIgnoreCase: BOOL,
992992
) -> c_int;
993+
pub fn GetFullPathNameW(
994+
lpFileName: LPCWSTR,
995+
nBufferLength: DWORD,
996+
lpBuffer: LPWSTR,
997+
lpFilePart: *mut LPWSTR,
998+
) -> DWORD;
993999
}
9941000

9951001
#[link(name = "ws2_32")]

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

+14-13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::sys::time::SystemTime;
1414
use crate::sys::{c, cvt};
1515
use crate::sys_common::{AsInner, FromInner, IntoInner};
1616

17+
use super::path::maybe_verbatim;
1718
use super::to_u16s;
1819

1920
pub struct File {
@@ -281,7 +282,7 @@ impl OpenOptions {
281282

282283
impl File {
283284
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
284-
let path = to_u16s(path)?;
285+
let path = maybe_verbatim(path)?;
285286
let handle = unsafe {
286287
c::CreateFileW(
287288
path.as_ptr(),
@@ -706,7 +707,7 @@ impl DirBuilder {
706707
}
707708

708709
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
709-
let p = to_u16s(p)?;
710+
let p = maybe_verbatim(p)?;
710711
cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?;
711712
Ok(())
712713
}
@@ -715,7 +716,7 @@ impl DirBuilder {
715716
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
716717
let root = p.to_path_buf();
717718
let star = p.join("*");
718-
let path = to_u16s(&star)?;
719+
let path = maybe_verbatim(&star)?;
719720

720721
unsafe {
721722
let mut wfd = mem::zeroed();
@@ -733,20 +734,20 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
733734
}
734735

735736
pub fn unlink(p: &Path) -> io::Result<()> {
736-
let p_u16s = to_u16s(p)?;
737+
let p_u16s = maybe_verbatim(p)?;
737738
cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
738739
Ok(())
739740
}
740741

741742
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
742-
let old = to_u16s(old)?;
743-
let new = to_u16s(new)?;
743+
let old = maybe_verbatim(old)?;
744+
let new = maybe_verbatim(new)?;
744745
cvt(unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) })?;
745746
Ok(())
746747
}
747748

748749
pub fn rmdir(p: &Path) -> io::Result<()> {
749-
let p = to_u16s(p)?;
750+
let p = maybe_verbatim(p)?;
750751
cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
751752
Ok(())
752753
}
@@ -794,7 +795,7 @@ pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
794795

795796
pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> {
796797
let original = to_u16s(original)?;
797-
let link = to_u16s(link)?;
798+
let link = maybe_verbatim(link)?;
798799
let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
799800
// Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
800801
// Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
@@ -823,8 +824,8 @@ pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()>
823824

824825
#[cfg(not(target_vendor = "uwp"))]
825826
pub fn link(original: &Path, link: &Path) -> io::Result<()> {
826-
let original = to_u16s(original)?;
827-
let link = to_u16s(link)?;
827+
let original = maybe_verbatim(original)?;
828+
let link = maybe_verbatim(link)?;
828829
cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
829830
Ok(())
830831
}
@@ -857,7 +858,7 @@ pub fn lstat(path: &Path) -> io::Result<FileAttr> {
857858
}
858859

859860
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
860-
let p = to_u16s(p)?;
861+
let p = maybe_verbatim(p)?;
861862
unsafe {
862863
cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
863864
Ok(())
@@ -900,8 +901,8 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
900901
}
901902
c::PROGRESS_CONTINUE
902903
}
903-
let pfrom = to_u16s(from)?;
904-
let pto = to_u16s(to)?;
904+
let pfrom = maybe_verbatim(from)?;
905+
let pto = maybe_verbatim(to)?;
905906
let mut size = 0i64;
906907
cvt(unsafe {
907908
c::CopyFileExW(

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

+97
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
use super::{c, fill_utf16_buf, to_u16s};
12
use crate::ffi::OsStr;
3+
use crate::io;
24
use crate::mem;
5+
use crate::path::Path;
36
use crate::path::Prefix;
7+
use crate::ptr;
48

59
#[cfg(test)]
610
mod tests;
@@ -141,3 +145,96 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
141145
None => (path, OsStr::new("")),
142146
}
143147
}
148+
149+
/// Returns a UTF-16 encoded path capable of bypassing the legacy `MAX_PATH` limits.
150+
///
151+
/// This path may or may not have a verbatim prefix.
152+
pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
153+
const LEGACY_MAX_PATH: usize = 260;
154+
// UTF-16 encoded code points, used in parsing and building UTF-16 paths.
155+
// All of these are in the ASCII range so they can be cast directly to `u16`.
156+
const SEP: u16 = b'\\' as _;
157+
const ALT_SEP: u16 = b'/' as _;
158+
const QUERY: u16 = b'?' as _;
159+
const COLON: u16 = b':' as _;
160+
const DOT: u16 = b'.' as _;
161+
const U: u16 = b'U' as _;
162+
const N: u16 = b'N' as _;
163+
const C: u16 = b'C' as _;
164+
165+
// \\?\
166+
const VERBATIM_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP];
167+
// \??\
168+
const NT_PREFIX: &[u16] = &[SEP, QUERY, QUERY, SEP];
169+
// \\?\UNC\
170+
const UNC_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP, U, N, C, SEP];
171+
172+
let mut path = to_u16s(path)?;
173+
if path.starts_with(VERBATIM_PREFIX) || path.starts_with(NT_PREFIX) {
174+
// Early return for paths that are already verbatim.
175+
return Ok(path);
176+
} else if path.len() < LEGACY_MAX_PATH {
177+
// Early return if an absolute path is less < 260 UTF-16 code units.
178+
// This is an optimization to avoid calling `GetFullPathNameW` unnecessarily.
179+
match path.as_slice() {
180+
// Starts with `D:`, `D:\`, `D:/`, etc.
181+
// Does not match if the path starts with a `\` or `/`.
182+
[drive, COLON, 0] | [drive, COLON, SEP | ALT_SEP, ..]
183+
if *drive != SEP && *drive != ALT_SEP =>
184+
{
185+
return Ok(path);
186+
}
187+
// Starts with `\\`, `//`, etc
188+
[SEP | ALT_SEP, SEP | ALT_SEP, ..] => return Ok(path),
189+
_ => {}
190+
}
191+
}
192+
193+
// Firstly, get the absolute path using `GetFullPathNameW`.
194+
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
195+
let lpfilename = path.as_ptr();
196+
fill_utf16_buf(
197+
// SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid.
198+
// `lpfilename` is a pointer to a null terminated string that is not
199+
// invalidated until after `GetFullPathNameW` returns successfully.
200+
|buffer, size| unsafe {
201+
// While the docs for `GetFullPathNameW` have the standard note
202+
// about needing a `\\?\` path for a long lpfilename, this does not
203+
// appear to be true in practice.
204+
// See:
205+
// https://stackoverflow.com/questions/38036943/getfullpathnamew-and-long-windows-file-paths
206+
// https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
207+
c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut())
208+
},
209+
|mut absolute| {
210+
path.clear();
211+
212+
// Secondly, add the verbatim prefix. This is easier here because we know the
213+
// path is now absolute and fully normalized (e.g. `/` has been changed to `\`).
214+
let prefix = match absolute {
215+
// C:\ => \\?\C:\
216+
[_, COLON, SEP, ..] => VERBATIM_PREFIX,
217+
// \\.\ => \\?\
218+
[SEP, SEP, DOT, SEP, ..] => {
219+
absolute = &absolute[4..];
220+
VERBATIM_PREFIX
221+
}
222+
// Leave \\?\ and \??\ as-is.
223+
[SEP, SEP, QUERY, SEP, ..] | [SEP, QUERY, QUERY, SEP, ..] => &[],
224+
// \\ => \\?\UNC\
225+
[SEP, SEP, ..] => {
226+
absolute = &absolute[2..];
227+
UNC_PREFIX
228+
}
229+
// Anything else we leave alone.
230+
_ => &[],
231+
};
232+
233+
path.reserve_exact(prefix.len() + absolute.len() + 1);
234+
path.extend_from_slice(prefix);
235+
path.extend_from_slice(absolute);
236+
path.push(0);
237+
},
238+
)?;
239+
Ok(path)
240+
}

library/std/src/sys/windows/path/tests.rs

+46
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,49 @@ fn test_parse_next_component() {
4242
(OsStr::new(r"server"), OsStr::new(r"\\\\\\\\\\\\\share"))
4343
);
4444
}
45+
46+
#[test]
47+
fn verbatim() {
48+
use crate::path::Path;
49+
fn check(path: &str, expected: &str) {
50+
let verbatim = maybe_verbatim(Path::new(path)).unwrap();
51+
let verbatim = String::from_utf16_lossy(verbatim.strip_suffix(&[0]).unwrap());
52+
assert_eq!(&verbatim, expected, "{}", path);
53+
}
54+
55+
// Ensure long paths are correctly prefixed.
56+
check(
57+
r"C:\Program Files\Rust\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
58+
r"\\?\C:\Program Files\Rust\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
59+
);
60+
check(
61+
r"\\server\share\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
62+
r"\\?\UNC\server\share\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
63+
);
64+
check(
65+
r"\\.\PIPE\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
66+
r"\\?\PIPE\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
67+
);
68+
// `\\?\` prefixed paths are left unchanged...
69+
check(
70+
r"\\?\verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
71+
r"\\?\verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
72+
);
73+
// But `//?/` is not a verbatim prefix so it will be normalized.
74+
check(
75+
r"//?/E:/verbatim.\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
76+
r"\\?\E:\verbatim\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\foo.txt",
77+
);
78+
79+
// For performance, short absolute paths are left unchanged.
80+
check(r"C:\Program Files\Rust", r"C:\Program Files\Rust");
81+
check(r"\\server\share", r"\\server\share");
82+
check(r"\\.\COM1", r"\\.\COM1");
83+
84+
// Make sure opening a drive will work.
85+
check("Z:", "Z:");
86+
87+
// An empty path or a path that contains null are not valid paths.
88+
assert!(maybe_verbatim(Path::new("")).is_err());
89+
assert!(maybe_verbatim(Path::new("\0")).is_err());
90+
}

0 commit comments

Comments
 (0)